removed Register expression (directly accessing cpu register)

This commit is contained in:
Irmen de Jong 2020-07-24 22:57:19 +02:00
parent 237511f2d6
commit 0c461ffe2e
61 changed files with 311 additions and 1604 deletions

View File

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

View File

@ -1 +1 @@
2.4
3.0

View File

@ -310,10 +310,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
override fun visit(forLoop: ForLoop) {
output("for ")
if(forLoop.loopRegister!=null)
output(forLoop.loopRegister.toString())
else
forLoop.loopVar!!.accept(this)
forLoop.loopVar.accept(this)
output(" in ")
forLoop.iterable.accept(this)
output(" ")
@ -352,12 +349,8 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
}
override fun visit(assignTarget: AssignTarget) {
if(assignTarget.register!=null)
output(assignTarget.register.toString())
else {
assignTarget.memoryAddress?.accept(this)
assignTarget.identifier?.accept(this)
}
assignTarget.memoryAddress?.accept(this)
assignTarget.identifier?.accept(this)
assignTarget.arrayindexed?.accept(this)
}
@ -398,10 +391,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
outputlni("}}")
}
override fun visit(registerExpr: RegisterExpr) {
output(registerExpr.register.toString())
}
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
output(builtinFunctionStatementPlaceholder.name)
}

View File

@ -247,7 +247,7 @@ private class AsmsubDecl(val name: String,
val returntypes: List<DataType>,
val asmParameterRegisters: List<RegisterOrStatusflag>,
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
val asmClobbers: Set<Register>)
val asmClobbers: Set<CpuRegister>)
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
val name = identifier().text
@ -274,24 +274,43 @@ private class AsmSubroutineReturn(val type: DataType,
val stack: Boolean,
val position: Position)
private fun prog8Parser.ClobberContext.toAst(): Set<Register>
= this.register().asSequence().map { it.toAst() }.toSet()
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
= asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) }
= asmsub_return().map {
val register = it.identifier()?.toAst()
var registerorpair: RegisterOrPair? = null
var statusregister: Statusflag? = null
if(register!=null) {
when (val name = register.nameInSource.single()) {
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
else -> throw FatalAstException("invalid register or status flag in $it")
}
}
AsmSubroutineReturn(
it.datatype().toAst(),
registerorpair,
statusregister,
!it.stack?.text.isNullOrEmpty(), toPosition())
}
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
= asmsub_param().map {
val vardecl = it.vardecl()
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
AsmSubroutineParameter(vardecl.varname.text, datatype,
it.registerorpair()?.toAst(),
it.statusregister()?.toAst(),
val register = it.identifier()?.toAst()
var registerorpair: RegisterOrPair? = null
var statusregister: Statusflag? = null
if(register!=null) {
when (val name = register.nameInSource.single()) {
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
else -> throw FatalAstException("invalid register or status flag in $it")
}
}
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister,
!it.stack?.text.isNullOrEmpty(), toPosition())
}
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
val void = this.VOID() != null
val location = scoped_identifier().toAst()
@ -350,23 +369,22 @@ private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
}
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
val register = register()?.toAst()
val identifier = scoped_identifier()
return when {
register!=null -> AssignTarget(register, null, null, null, toPosition())
identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition())
arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition())
directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition())
arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(), null, toPosition())
directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
else -> AssignTarget(scoped_identifier()?.toAst(), null, null, toPosition())
}
}
private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase())
private fun prog8Parser.ClobberContext.toAst() : Set<CpuRegister> {
val names = this.identifier().map { it.toAst().nameInSource.single() }
return names.map { CpuRegister.valueOf(it) }.toSet()
}
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
ArrayIndex(expression().toAst(), toPosition())
@ -478,9 +496,6 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
}
}
if(register()!=null)
return RegisterExpr(register().toAst(), register().toPosition())
if(scoped_identifier()!=null)
return scoped_identifier().toAst()
@ -572,15 +587,14 @@ private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
val loopregister = register()?.toAst()
val loopvar = identifier()?.toAst()
val loopvar = identifier().toAst()
val iterable = expression()!!.toAst()
val scope =
if(statement()!=null)
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
else
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
return ForLoop(loopregister, loopvar, iterable, scope, toPosition())
return ForLoop(loopvar, iterable, scope, toPosition())
}
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())

View File

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

View File

@ -28,22 +28,20 @@ sealed class Expression: Node {
infix fun isSameAs(other: Expression): Boolean {
if(this===other)
return true
when(this) {
is RegisterExpr ->
return (other is RegisterExpr && other.register==register)
return when(this) {
is IdentifierReference ->
return (other is IdentifierReference && other.nameInSource==nameInSource)
(other is IdentifierReference && other.nameInSource==nameInSource)
is PrefixExpression ->
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
(other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
is BinaryExpression ->
return (other is BinaryExpression && other.operator==operator
(other is BinaryExpression && other.operator==operator
&& other.left isSameAs left
&& other.right isSameAs right)
is ArrayIndexedExpression -> {
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
(other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
&& other.arrayspec.index isSameAs arrayspec.index)
}
else -> return other==this
else -> other==this
}
}
}
@ -696,29 +694,6 @@ internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
}
}
class RegisterExpr(val register: Register, override val position: Position) : Expression(), IAssignable {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
}
override fun replaceChildNode(node: Node, replacement: Node) {
throw FatalAstException("can't replace here")
}
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name
override fun toString(): String {
return "RegisterExpr(register=$register, pos=$position)"
}
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
}
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
override lateinit var parent: Node
@ -732,6 +707,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource
override fun hashCode() = nameInSource.hashCode()
override fun linkParents(parent: Node) {
this.parent = parent

View File

@ -117,42 +117,33 @@ internal class AstChecker(private val program: Program,
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
errors.err("can only loop over an iterable type", forLoop.position)
} else {
if (forLoop.loopRegister != null) {
// loop register
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt != DataType.STR)
errors.err("register can only loop over bytes", forLoop.position)
if(forLoop.loopRegister!=Register.A)
errors.err("it's only possible to use A as a loop register", forLoop.position)
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position)
} else {
// loop variable
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position)
} else {
when (loopvar.datatype) {
DataType.UBYTE -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
}
DataType.UWORD -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
}
DataType.BYTE -> {
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
errors.err("byte loop variable can only loop over bytes", forLoop.position)
}
DataType.WORD -> {
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
}
DataType.FLOAT -> {
errors.err("for loop only supports integers", forLoop.position)
}
else -> errors.err("loop variable must be numeric type", forLoop.position)
when (loopvar.datatype) {
DataType.UBYTE -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
}
DataType.UWORD -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
}
DataType.BYTE -> {
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
errors.err("byte loop variable can only loop over bytes", forLoop.position)
}
DataType.WORD -> {
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
}
DataType.FLOAT -> {
errors.err("for loop only supports integers", forLoop.position)
}
else -> errors.err("loop variable must be numeric type", forLoop.position)
}
}
}
@ -260,27 +251,27 @@ internal class AstChecker(private val program: Program,
}
}
val regCounts = mutableMapOf<Register, Int>().withDefault { 0 }
val regCounts = mutableMapOf<CpuRegister, Int>().withDefault { 0 }
val statusflagCounts = mutableMapOf<Statusflag, Int>().withDefault { 0 }
fun countRegisters(from: Iterable<RegisterOrStatusflag>) {
regCounts.clear()
statusflagCounts.clear()
for(p in from) {
when(p.registerOrPair) {
RegisterOrPair.A -> regCounts[Register.A]=regCounts.getValue(Register.A)+1
RegisterOrPair.X -> regCounts[Register.X]=regCounts.getValue(Register.X)+1
RegisterOrPair.Y -> regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
RegisterOrPair.A -> regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
RegisterOrPair.X -> regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
RegisterOrPair.Y -> regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
RegisterOrPair.AX -> {
regCounts[Register.A]=regCounts.getValue(Register.A)+1
regCounts[Register.X]=regCounts.getValue(Register.X)+1
regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
}
RegisterOrPair.AY -> {
regCounts[Register.A]=regCounts.getValue(Register.A)+1
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
}
RegisterOrPair.XY -> {
regCounts[Register.X]=regCounts.getValue(Register.X)+1
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
}
null ->
if(p.statusflag!=null)
@ -326,16 +317,12 @@ internal class AstChecker(private val program: Program,
}
override fun visit(repeatLoop: RepeatLoop) {
if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y"))
errors.warn("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position)
if(repeatLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", repeatLoop.untilCondition.position)
super.visit(repeatLoop)
}
override fun visit(whileLoop: WhileLoop) {
if(whileLoop.condition.referencesIdentifiers("A", "X", "Y"))
errors.warn("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position)
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", whileLoop.condition.position)
super.visit(whileLoop)
@ -397,8 +384,7 @@ internal class AstChecker(private val program: Program,
val targetIdentifier = assignTarget.identifier
if (targetIdentifier != null) {
val targetName = targetIdentifier.nameInSource
val targetSymbol = program.namespace.lookup(targetName, assignment)
when (targetSymbol) {
when (val targetSymbol = program.namespace.lookup(targetName, assignment)) {
null -> {
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
return
@ -844,7 +830,7 @@ internal class AstChecker(private val program: Program,
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
// in-place modification, can't be done on literals
if(functionCallStatement.args.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
}
}

View File

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

View File

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

View File

@ -110,7 +110,6 @@ abstract class AstWalker {
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
@ -154,7 +153,6 @@ abstract class AstWalker {
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
@ -325,7 +323,7 @@ abstract class AstWalker {
fun visit(forLoop: ForLoop, parent: Node) {
track(before(forLoop, parent), forLoop, parent)
forLoop.loopVar?.accept(this, forLoop)
forLoop.loopVar.accept(this, forLoop)
forLoop.iterable.accept(this, forLoop)
forLoop.body.accept(this, forLoop)
track(after(forLoop, parent), forLoop, parent)
@ -407,11 +405,6 @@ abstract class AstWalker {
track(after(inlineAssembly, parent), inlineAssembly, parent)
}
fun visit(registerExpr: RegisterExpr, parent: Node) {
track(before(registerExpr, parent), registerExpr, parent)
track(after(registerExpr, parent), registerExpr, parent)
}
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) {
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)

View File

@ -102,7 +102,7 @@ interface IAstVisitor {
}
fun visit(forLoop: ForLoop) {
forLoop.loopVar?.accept(this)
forLoop.loopVar.accept(this)
forLoop.iterable.accept(this)
forLoop.body.accept(this)
}
@ -159,9 +159,6 @@ interface IAstVisitor {
fun visit(inlineAssembly: InlineAssembly) {
}
fun visit(registerExpr: RegisterExpr) {
}
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
}

View File

@ -78,7 +78,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
if(declConstValue==null) {
// move the vardecl (without value) to the scope and replace this with a regular assignment
decl.value = null
val target = AssignTarget(null, IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
val assign = Assignment(target, null, declValue, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, assign, parent),
@ -136,7 +136,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
targetDecl as VarDecl
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
null, sourceValue, sourceValue.position)
assign.linkParents(structAssignment)
assign
@ -168,7 +168,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(null, idref, null, null, structAssignment.position),
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
null, sourceIdref, member.second.position)
assign.linkParents(structAssignment)
assign

View File

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

View File

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

View File

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

View File

@ -183,7 +183,7 @@ internal class AsmGen(private val program: Program,
blockLevelVarInits.getValue(block).forEach { decl ->
val scopedFullName = decl.makeScopedName(decl.name).split('.')
require(scopedFullName.first()==block.name)
val target = AssignTarget(null, IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
val target = AssignTarget(IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
val assign = Assignment(target, null, decl.value!!, decl.position)
assign.linkParents(decl.parent)
assignmentAsmGen.translate(assign)
@ -561,19 +561,19 @@ internal class AsmGen(private val program: Program,
}
}
internal fun saveRegister(register: Register) {
internal fun saveRegister(register: CpuRegister) {
when(register) {
Register.A -> out(" pha")
Register.X -> out(" txa | pha")
Register.Y -> out(" tya | pha")
CpuRegister.A -> out(" pha")
CpuRegister.X -> out(" txa | pha")
CpuRegister.Y -> out(" tya | pha")
}
}
internal fun restoreRegister(register: Register) {
internal fun restoreRegister(register: CpuRegister) {
when(register) {
Register.A -> out(" pla")
Register.X -> out(" pla | tax")
Register.Y -> out(" pla | tay")
CpuRegister.A -> out(" pla")
CpuRegister.X -> out(" pla | tax")
CpuRegister.Y -> out(" pla | tay")
}
}
@ -836,7 +836,7 @@ internal class AsmGen(private val program: Program,
}
inits.add(stmt)
} else {
val target = AssignTarget(null, IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
val target = AssignTarget(IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
val assign = Assignment(target, null, stmt.value!!, stmt.position)
assign.linkParents(stmt.parent)
translate(assign)
@ -901,13 +901,6 @@ internal class AsmGen(private val program: Program,
internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) {
when (val index = expr.arrayspec.index) {
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
is RegisterExpr -> {
when (index.register) {
Register.A -> {}
Register.X -> out(" txa")
Register.Y -> out(" tya")
}
}
is IdentifierReference -> {
val indexName = asmIdentifierName(index)
out(" lda $indexName")
@ -923,13 +916,6 @@ internal class AsmGen(private val program: Program,
internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) {
when (val index = expr.arrayspec.index) {
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
is RegisterExpr -> {
when (index.register) {
Register.A -> out(" tay")
Register.X -> out(" txa | tay")
Register.Y -> {}
}
}
is IdentifierReference -> {
val indexName = asmIdentifierName(index)
out(" ldy $indexName")
@ -972,9 +958,12 @@ internal class AsmGen(private val program: Program,
fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromFloatVariable(target, variable)
fun assignFromRegister(target: AssignTarget, register: Register) =
fun assignFromRegister(target: AssignTarget, register: CpuRegister) =
assignmentAsmGen.assignFromRegister(target, register)
fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) =
assignmentAsmGen.assignFromMemoryByte(target, address, identifier)
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
assignmentAsmGen.assignToRegister(reg, value, identifier)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -342,40 +342,38 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
val rangeTo = iterableRange.to as? NumericLiteralValue
if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar?.targetVarDecl(program.namespace)
if(loopvar!=null) {
val stepLiteral = iterableRange.step as? NumericLiteralValue
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
// attempt to translate the iterable into ubyte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)!!
val stepLiteral = iterableRange.step as? NumericLiteralValue
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
// attempt to translate the iterable into ubyte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
DataType.BYTE -> {
if(rangeFrom.type!= DataType.BYTE) {
// attempt to translate the iterable into byte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.UWORD -> {
if(rangeFrom.type!= DataType.UWORD) {
// attempt to translate the iterable into uword values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.WORD -> {
if(rangeFrom.type!= DataType.WORD) {
// attempt to translate the iterable into word values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
}
DataType.BYTE -> {
if(rangeFrom.type!= DataType.BYTE) {
// attempt to translate the iterable into byte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.UWORD -> {
if(rangeFrom.type!= DataType.UWORD) {
// attempt to translate the iterable into uword values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.WORD -> {
if(rangeFrom.type!= DataType.WORD) {
// attempt to translate the iterable into word values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
}
return noModifications

View File

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

View File

@ -46,7 +46,11 @@ internal class StatementOptimizer(private val program: Program,
if(subroutine.asmAddress==null && !forceOutput) {
if(subroutine.containsNoCodeNorVars()) {
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
return listOf(IAstModification.Remove(subroutine, parent))
val removals = callgraph.calledBy.getValue(subroutine).map {
IAstModification.Remove(it, it.parent)
}.toMutableList()
removals += IAstModification.Remove(subroutine, parent)
return removals
}
}
@ -195,7 +199,7 @@ internal class StatementOptimizer(private val program: Program,
return listOf(IAstModification.Remove(forLoop, parent))
} else if(forLoop.body.statements.size==1) {
val loopvar = forLoop.body.statements[0] as? VarDecl
if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) {
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
// remove empty for loop (only loopvar decl in it)
return listOf(IAstModification.Remove(forLoop, parent))
}
@ -207,7 +211,7 @@ internal class StatementOptimizer(private val program: Program,
// for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block
val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position))
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
}
@ -222,7 +226,7 @@ internal class StatementOptimizer(private val program: Program,
val character = CompilationTarget.encodeString(sv.value, sv.altEncoding)[0]
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, byte, forLoop.position))
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, byte, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
}
@ -235,7 +239,7 @@ internal class StatementOptimizer(private val program: Program,
if(av!=null) {
val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(
AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))

View File

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

View File

@ -204,13 +204,6 @@ Example::
byte @zp zeropageCounter = 42
Variables that represent CPU hardware registers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following variables are reserved
and map directly (read/write) to a CPU hardware register: ``A``, ``X``, ``Y``.
Integers
^^^^^^^^
@ -393,7 +386,7 @@ expected when the program is restarted.
Loops
-----
The *for*-loop is used to let a variable (or register) iterate over a range of values. Iteration is done in steps of 1, but you can change this.
The *for*-loop is used to let a variable iterate over a range of values. Iteration is done in steps of 1, but you can change this.
The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions.
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
@ -407,7 +400,7 @@ a forever-loop.
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
.. attention::
The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately
The value of the loop variable after executing the loop *is undefined*. Don't use it immediately
after the loop without first assigning a new value to it!
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
@ -421,15 +414,15 @@ if statements
Conditional execution means that the flow of execution changes based on certiain conditions,
rather than having fixed gotos or subroutine calls::
if A>4 goto overflow
if aa>4 goto overflow
if X==3 Y = 4
if X==3 Y = 4 else A = 2
if xx==3 yy = 4
if xx==3 yy = 4 else aa = 2
if X==5 {
Y = 99
if xx==5 {
yy = 99
} else {
A = 3
aa = 3
}
@ -493,16 +486,16 @@ Assignments
-----------
Assignment statements assign a single value to a target variable or memory location.
Augmented assignments (such as ``A += X``) are also available, but these are just shorthands
for normal assignments (``A = A + X``).
Augmented assignments (such as ``aa += xx``) are also available, but these are just shorthands
for normal assignments (``aa = aa + xx``).
Only register variables and variables of type byte, word and float can be assigned a new value.
Only variables of type byte, word and float can be assigned a new value.
It's not possible to set a new value to string or array variables etc, because they get allocated
a fixed amount of memory which will not change.
a fixed amount of memory which will not change. (You *can* change the value of elements in a string or array though).
.. attention::
**Data type conversion (in assignments):**
When assigning a value with a 'smaller' datatype to a register or variable with a 'larger' datatype,
When assigning a value with a 'smaller' datatype to variable with a 'larger' datatype,
the value will be automatically converted to the target datatype: byte --> word --> float.
So assigning a byte to a word variable, or a word to a floating point variable, is fine.
The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to
@ -518,7 +511,7 @@ as the memory mapped address $d021.
If you want to access a memory location directly (by using the address itself), without defining
a memory mapped location, you can do so by enclosing the address in ``@(...)``::
A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)")
color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address

View File

@ -24,7 +24,7 @@ Everything after a semicolon ``;`` is a comment and is ignored.
If the whole line is just a comment, it will be copied into the resulting assembly source code.
This makes it easier to understand and relate the generated code. Examples::
A = 42 ; set the initial value to 42
counter = 42 ; set the initial value to 42
; next is the code that...
@ -313,7 +313,7 @@ Direct access to memory locations
Instead of defining a memory mapped name for a specific memory location, you can also
directly access the memory. Enclose a numeric expression or literal with ``@(...)`` to do that::
A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)")
color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
@ -333,8 +333,6 @@ Reserved names
The following names are reserved, they have a special meaning::
A X Y ; 6502 hardware registers
Pc Pz Pn Pv ; 6502 status register flags
true false ; boolean values 1 and 0
@ -406,10 +404,10 @@ assignment: ``=``
Note that an assignment sometimes is not possible or supported.
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
Syntactic sugar; ``A += X`` is equivalent to ``A = A + X``
Syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
postfix increment and decrement: ``++`` ``--``
Syntactic sugar; ``A++`` is equivalent to ``A = A + 1``, and ``A--`` is equivalent to ``A = A - 1``.
Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``.
Because these operations are so common, we have these short forms.
comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=``
@ -427,9 +425,9 @@ range creation: ``to``
0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant)
A = 5
X = 10
A to X ; range of 5, 6, 7, 8, 9, 10
aa = 5
aa = 10
aa to xx ; range of 5, 6, 7, 8, 9, 10
byte[] array = 10 to 13 ; sets the array to [1, 2, 3, 4]
@ -551,7 +549,7 @@ Loops
for loop
^^^^^^^^
The loop variable must be a register or a byte/word variable,
The loop variable must be a byte or word variable,
and must be defined first in the local scope of the for loop.
The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``,
array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported).
@ -702,3 +700,4 @@ case you have to use { } to enclose them::
}
else -> c64scr.print("don't know")
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,11 @@
main {
sub start() {
c64scr.print("fibonacci sequence\n")
for A in 0 to 20 {
; TODO fix the double i=0 assignment generation in the asm code:
ubyte i
for i in 0 to 20 {
c64scr.print_uw(fib_next())
c64.CHROUT('\n')
}

View File

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

View File

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

View File

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

View File

@ -24,8 +24,6 @@ main {
c64scr.print("number of primes (expected 54): ")
c64scr.print_ub(amount)
c64.CHROUT('\n')
check_eval_stack()
}
@ -48,14 +46,4 @@ main {
}
return candidate_prime
}
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,5 +8,6 @@
main {
sub start() {
@($d020)=0
}
}

View File

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

View File

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

View File

@ -158,8 +158,7 @@ augassignment :
;
assign_target:
register
| scoped_identifier
scoped_identifier
| arrayindexed
| directmemory
;
@ -184,7 +183,6 @@ expression :
| left = expression EOL? bop = 'xor' EOL? right = expression
| prefix = 'not' expression
| literalvalue
| register
| scoped_identifier
| arrayindexed
| directmemory
@ -222,12 +220,6 @@ identifier : NAME ;
scoped_identifier : NAME ('.' NAME)* ;
register : 'A' | 'X' | 'Y' ;
registerorpair : 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' ; // pairs can only be used in subroutine params and returnvalues
statusregister : 'Pc' | 'Pz' | 'Pn' | 'Pv' ;
integerliteral : intpart=(DEC_INTEGER | HEX_INTEGER | BIN_INTEGER) wordsuffix? ;
wordsuffix : '.w' ;
@ -287,15 +279,15 @@ asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns?
asmsub_params : asmsub_param (',' EOL? asmsub_param)* ;
asmsub_param : vardecl '@' (registerorpair | statusregister | stack='stack') ;
asmsub_param : vardecl '@' (identifier | stack='stack') ; // A,X,Y,AX,AY,XY,Pc,Pz,Pn,Pv allowed
asmsub_clobbers : 'clobbers' '(' clobber? ')' ;
clobber : register (',' register)* ;
clobber : identifier (',' identifier)* ; // A,X,Y allowed
asmsub_returns : '->' asmsub_return (',' EOL? asmsub_return)* ;
asmsub_return : datatype '@' (registerorpair | statusregister | stack='stack') ;
asmsub_return : datatype '@' (identifier | stack='stack') ; // A,X,Y,AX,AY,XY,Pc,Pz,Pn,Pv allowed
if_stmt : 'if' expression EOL? (statement | statement_block) EOL? else_part? ; // statement is constrained later
@ -308,7 +300,7 @@ branch_stmt : branchcondition EOL? (statement | statement_block) EOL? else_part?
branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ;
forloop : 'for' (register | identifier) 'in' expression EOL? (statement | statement_block) ;
forloop : 'for' identifier 'in' expression EOL? (statement | statement_block) ;
whileloop: 'while' expression EOL? (statement | statement_block) ;