Merge branch 'initonce-var-tag'

# Conflicts:
#	examples/test.p8
This commit is contained in:
Irmen de Jong
2024-11-08 19:23:44 +01:00
17 changed files with 126 additions and 137 deletions

View File

@@ -76,7 +76,7 @@ class VarConstantValueTypeAdjuster(
if (declValue != null) { if (declValue != null) {
// variable is never written to, so it can be replaced with a constant, IF the value is a constant // variable is never written to, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position) errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.position) val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.initOnce, decl.position)
decl.value = null decl.value = null
return listOf( return listOf(
IAstModification.ReplaceNode(decl, const, parent) IAstModification.ReplaceNode(decl, const, parent)
@@ -96,7 +96,7 @@ class VarConstantValueTypeAdjuster(
} }
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant // variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position) errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.position) val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.initOnce, decl.position)
return listOf( return listOf(
IAstModification.ReplaceNode(decl, const, parent), IAstModification.ReplaceNode(decl, const, parent),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer) IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
@@ -398,7 +398,7 @@ internal class ConstantIdentifierReplacer(
if(targetDatatype.isArray) { if(targetDatatype.isArray) {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOr(DataType.UNDEFINED), val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOr(DataType.UNDEFINED),
ZeropageWish.DONTCARE, null, "dummy", emptyList(), ZeropageWish.DONTCARE, null, "dummy", emptyList(),
assignment.value, false, false, 0u, Position.DUMMY) assignment.value, false, false, 0u, false, Position.DUMMY)
val replaceValue = createConstArrayInitializerValue(decl) val replaceValue = createConstArrayInitializerValue(decl)
if(replaceValue!=null) { if(replaceValue!=null) {
return listOf(IAstModification.ReplaceNode(assignment.value, replaceValue, assignment)) return listOf(IAstModification.ReplaceNode(assignment.value, replaceValue, assignment))
@@ -419,6 +419,21 @@ internal class ConstantIdentifierReplacer(
return null return null
} }
if (decl.isArray && decl.initOnce) {
if (decl.value == null) {
// initonce array without initialization value, make an array of just zeros
val size = decl.arraysize?.constIndex()
if(size!=null) {
val zeros = Array<Expression>(size) { decl.zeroElementValue() }
val initvalue = ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), zeros, decl.position)
decl.value = initvalue
initvalue.linkParents(decl)
}
} else {
errors.err("arrays with an initialization value already are initialized only once by default", decl.position)
}
}
val rangeExpr = decl.value as? RangeExpression ?: return null val rangeExpr = decl.value as? RangeExpression ?: return null
// convert the initializer range expression from a range, to an actual array literal. // convert the initializer range expression from a range, to an actual array literal.

View File

@@ -919,6 +919,18 @@ internal class AstChecker(private val program: Program,
} }
} }
if(decl.datatype==DataType.STR) {
if(!decl.initOnce)
throw FatalAstException("string vars must be initonce")
}
if (decl.initOnce) {
if (decl.datatype != DataType.STR) {
errors.warn("non-string initonce variable: value will not be reset in subsequent subroutine invocations", decl.position)
}
}
super.visit(decl) super.visit(decl)
} }

View File

@@ -206,7 +206,7 @@ class AstPreprocessor(val program: Program,
} }
val newDecl = VarDecl( val newDecl = VarDecl(
decl.type, decl.origin, splitDt, decl.zeropage, decl.arraysize, decl.name, emptyList(), decl.type, decl.origin, splitDt, decl.zeropage, decl.arraysize, decl.name, emptyList(),
decl.value?.copy(), decl.sharedWithAsm, true, decl.alignment, decl.position decl.value?.copy(), decl.sharedWithAsm, true, decl.alignment, false, decl.position
) )
return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent)) return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent))
} }

View File

@@ -26,8 +26,10 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
} }
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if (decl.type == VarDeclType.VAR && decl.value != null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL)) if (decl.type == VarDeclType.VAR && decl.value != null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL)) {
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl") if(!decl.initOnce)
throw InternalCompilerException("vardecls for non-initonce variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
}
return noModifications return noModifications
} }

View File

@@ -528,6 +528,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
when(srcVar.type) { when(srcVar.type) {
VarDeclType.VAR -> { VarDeclType.VAR -> {
val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null
if(srcVar.initOnce && value==null)
throw FatalAstException("initonce without value $srcVar")
return PtVariable( return PtVariable(
srcVar.name, srcVar.name,
srcVar.datatype, srcVar.datatype,

View File

@@ -180,7 +180,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
} }
return VarDecl( return VarDecl(
variable.type, variable.origin, normalDt, variable.zeropage, variable.arraysize, variable.name, emptyList(), variable.type, variable.origin, normalDt, variable.zeropage, variable.arraysize, variable.name, emptyList(),
variable.value?.copy(), variable.sharedWithAsm, false, variable.alignment, variable.position variable.value?.copy(), variable.sharedWithAsm, false, variable.alignment, variable.initOnce, variable.position
) )
} }

View File

@@ -48,6 +48,12 @@ internal class StatementReorderer(
declsProcessedWithInitAssignment.add(decl) declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) { if (decl.value == null) {
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) { if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
if(decl.initOnce) {
val zerovalue = decl.zeroElementValue()
decl.value = zerovalue
zerovalue.linkParents(decl)
return noModifications
}
// A numeric vardecl without an initial value is initialized with zero, // A numeric vardecl without an initial value is initialized with zero,
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar). // unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
// This allows you to restart the program and have the same starting values of the variables // This allows you to restart the program and have the same starting values of the variables
@@ -66,6 +72,9 @@ internal class StatementReorderer(
} }
} }
} else { } else {
if(decl.initOnce) {
return noModifications
}
// Transform the vardecl with initvalue to a plain vardecl + assignment // Transform the vardecl with initvalue to a plain vardecl + assignment
// this allows for other optimizations to kick in. // this allows for other optimizations to kick in.
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99' // So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
@@ -222,6 +231,7 @@ internal class StatementReorderer(
it.sharedWithAsm, it.sharedWithAsm,
it.splitArray, it.splitArray,
it.alignment, it.alignment,
it.initOnce,
it.position it.position
) )
IAstModification.ReplaceNode(it, newvar, subroutine) IAstModification.ReplaceNode(it, newvar, subroutine)

View File

@@ -114,7 +114,7 @@ class TestMemory: FunSpec({
} }
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget { fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, Position.DUMMY) val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, false, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY) val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@@ -152,7 +152,7 @@ class TestMemory: FunSpec({
} }
test("regular variable not in mapped IO ram on C64") { test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, 0u, Position.DUMMY) val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, 0u, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@@ -164,7 +164,7 @@ class TestMemory: FunSpec({
test("memory mapped variable not in mapped IO ram on C64") { test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, Position.DUMMY) val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@@ -176,7 +176,7 @@ class TestMemory: FunSpec({
test("memory mapped variable in mapped IO ram on C64") { test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u val address = 0xd020u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, Position.DUMMY) val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@@ -187,7 +187,7 @@ class TestMemory: FunSpec({
} }
test("array not in mapped IO ram") { test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, 0u, Position.DUMMY) val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@@ -200,7 +200,7 @@ class TestMemory: FunSpec({
test("memory mapped array not in mapped IO ram") { test("memory mapped array not in mapped IO ram") {
val address = 0x1000u val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, Position.DUMMY) val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@@ -213,7 +213,7 @@ class TestMemory: FunSpec({
test("memory mapped array in mapped IO ram") { test("memory mapped array in mapped IO ram") {
val address = 0xd800u val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, Position.DUMMY) val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)

View File

@@ -46,8 +46,8 @@ class TestAsmGenSymbols: StringSpec({
} }
*/ */
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", emptyList(), NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, 0u, Position.DUMMY) val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", emptyList(), NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, 0u, false, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, false, false, 0u, Position.DUMMY) val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, false, false, 0u, false, Position.DUMMY)
val labelInSub = Label("locallabel", Position.DUMMY) val labelInSub = Label("locallabel", Position.DUMMY)
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, null, false, Position.DUMMY) val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, null, false, Position.DUMMY)
@@ -63,7 +63,7 @@ class TestAsmGenSymbols: StringSpec({
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8) val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
val subroutine = Subroutine("start", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY) val subroutine = Subroutine("start", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)
val labelInBlock = Label("label_outside", Position.DUMMY) val labelInBlock = Label("label_outside", Position.DUMMY)
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", emptyList(),null, false, false, 0u, Position.DUMMY) val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", emptyList(),null, false, false, 0u, false, Position.DUMMY)
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY) val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))

View File

@@ -87,7 +87,7 @@ class Program(val name: String,
val varName = "string_${internedStringsBlock.statements.size}" val varName = "string_${internedStringsBlock.statements.size}"
val decl = VarDecl( val decl = VarDecl(
VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, emptyList(), string, VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, emptyList(), string,
sharedWithAsm = false, splitArray = false, alignment = 0u, position = string.position sharedWithAsm = false, splitArray = false, alignment = 0u, initOnce = true, position = string.position
) )
internedStringsBlock.statements.add(decl) internedStringsBlock.statements.add(decl)
decl.linkParents(internedStringsBlock) decl.linkParents(internedStringsBlock)

View File

@@ -328,6 +328,8 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
val options = it.decloptions() val options = it.decloptions()
if(options.ALIGNPAGE().isNotEmpty() || options.ALIGNWORD().isNotEmpty()) if(options.ALIGNPAGE().isNotEmpty() || options.ALIGNWORD().isNotEmpty())
throw SyntaxError("cannot use alignments on parameters", it.toPosition()) throw SyntaxError("cannot use alignments on parameters", it.toPosition())
if(options.INITONCE().isNotEmpty())
throw SyntaxError("cannot use @initonce on parameters", it.toPosition())
val zp = getZpOption(options) val zp = getZpOption(options)
var datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED var datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
if(it.ARRAYSIG()!=null || it.arrayindex()!=null) if(it.ARRAYSIG()!=null || it.arrayindex()!=null)
@@ -769,6 +771,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
options.SHARED().isNotEmpty(), options.SHARED().isNotEmpty(),
split, split,
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u, if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
dt==DataType.STR || options.INITONCE().isNotEmpty(),
toPosition() toPosition()
) )
} }

View File

@@ -249,6 +249,7 @@ class VarDecl(val type: VarDeclType,
val sharedWithAsm: Boolean, val sharedWithAsm: Boolean,
val splitArray: Boolean, val splitArray: Boolean,
val alignment: UInt, val alignment: UInt,
val initOnce: Boolean,
override val position: Position) : Statement(), INamedStatement { override val position: Position) : Statement(), INamedStatement {
override lateinit var parent: Node override lateinit var parent: Node
var allowInitializeWithZero = true var allowInitializeWithZero = true
@@ -262,6 +263,7 @@ class VarDecl(val type: VarDeclType,
sharedWithAsm = false, sharedWithAsm = false,
splitArray = false, splitArray = false,
alignment = 0u, alignment = 0u,
initOnce = false,
position = param.position position = param.position
) )
} }
@@ -271,10 +273,15 @@ class VarDecl(val type: VarDeclType,
val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") } val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
val arraysize = ArrayIndex.forArray(array) val arraysize = ArrayIndex.forArray(array)
return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, emptyList(), array, return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, emptyList(), array,
sharedWithAsm = false, splitArray = splitArray, alignment = 0u, position = array.position) sharedWithAsm = false, splitArray = splitArray, alignment = 0u, initOnce = false, position = array.position)
} }
} }
init {
if(datatype==DataType.STR && origin!=VarDeclOrigin.SUBROUTINEPARAM)
require(initOnce) { "string variable must be initonce" }
}
init { init {
if(datatype in SplitWordArrayTypes) if(datatype in SplitWordArrayTypes)
require(splitArray) require(splitArray)
@@ -313,7 +320,7 @@ class VarDecl(val type: VarDeclType,
if(names.size>1) if(names.size>1)
throw FatalAstException("should not copy a vardecl that still has multiple names") throw FatalAstException("should not copy a vardecl that still has multiple names")
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), name, names, value?.copy(), val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), name, names, value?.copy(),
sharedWithAsm, splitArray, alignment, position) sharedWithAsm, splitArray, alignment, initOnce, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero copy.allowInitializeWithZero = this.allowInitializeWithZero
return copy return copy
} }
@@ -328,19 +335,19 @@ class VarDecl(val type: VarDeclType,
// just copy the initialization value to a separate vardecl for each component // just copy the initialization value to a separate vardecl for each component
return names.map { return names.map {
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), value?.copy(), val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), value?.copy(),
sharedWithAsm, splitArray, alignment, position) sharedWithAsm, splitArray, alignment, initOnce, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero copy.allowInitializeWithZero = this.allowInitializeWithZero
copy copy
} }
} else { } else {
// evaluate the value once in the vardecl for the first component, and set the other components to the first // evaluate the value once in the vardecl for the first component, and set the other components to the first
val first = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), names[0], emptyList(), value?.copy(), val first = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), names[0], emptyList(), value?.copy(),
sharedWithAsm, splitArray, alignment, position) sharedWithAsm, splitArray, alignment, initOnce, position)
first.allowInitializeWithZero = this.allowInitializeWithZero first.allowInitializeWithZero = this.allowInitializeWithZero
val firstVar = firstVarAsValue(first) val firstVar = firstVarAsValue(first)
return listOf(first) + names.drop(1 ).map { return listOf(first) + names.drop(1 ).map {
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), firstVar.copy(), val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), firstVar.copy(),
sharedWithAsm, splitArray, alignment, position) sharedWithAsm, splitArray, alignment, initOnce, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero copy.allowInitializeWithZero = this.allowInitializeWithZero
copy copy
} }

View File

@@ -1,6 +1,10 @@
TODO TODO
==== ====
- BUG: fix @initonce for variables that end up in zeropage.
- add unit tests for @initonce variables
- add docs about variables with @initonce initialization
for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed. for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed.
@@ -36,7 +40,7 @@ Future Things and Ideas
- (What, how, isn't current BSS support enough?) - (What, how, isn't current BSS support enough?)
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class) - Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)
- maybe treat block "golden" in a special way: can only contain vars, every var will be allocated in the Golden ram area? - maybe treat block "golden" in a special way: can only contain vars, every var will be allocated in the Golden ram area?
- maybe or may not needed: the variables can NOT have initializfation values, they will all be set to zero on startup (simple memset) - maybe or may not needed: the variables can NOT have initialization values, they will all be set to zero on startup (simple memset)
just initialize them yourself in start() if you need a non-zero value . just initialize them yourself in start() if you need a non-zero value .
- OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram - OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram
- OR.... make all this more generic and use some %segment option to create real segments for 64tass? - OR.... make all this more generic and use some %segment option to create real segments for 64tass?

View File

@@ -1,118 +1,50 @@
%output raw %import textio
%launcher none %option no_sysinit
%zeropage dontuse
main { main {
sub start() { sub start() {
romsub $fff1 = WriteCharacter(ubyte character @A) uword w0
uword @initonce w1
uword @initonce w2
uword @initonce w3
uword @initonce w4 = 12345
uword[4] wa
uword[] @shared wb = [1111,2222,3333,4444]
for cx16.r0L in "\n\n\n.... Hello from Prog8 :-)" dump()
WriteCharacter(cx16.r0L) txt.nl()
w0++
w1++
w2++
w3++
w4++
wa[1]++
wa[2]++
wa[3]++
wb[0]++
dump()
txt.nl()
repeat { sub dump() {
txt.print_uw(w0)
txt.spc()
txt.print_uw(w1)
txt.spc()
txt.print_uw(w2)
txt.spc()
txt.print_uw(w3)
txt.spc()
txt.print_uw(w4)
txt.spc()
txt.print_uw(wa[1])
txt.spc()
txt.print_uw(wa[2])
txt.spc()
txt.print_uw(wa[3])
txt.spc()
txt.print_uw(wb[0])
txt.nl()
} }
} }
sub start2() {
%asm {{
; Program constants
CURSOR_POS_X = #0 ; character display 'X' coordinate
CURSOR_POS_Y = #21 ; character display 'Y' coordinate
NEWLINE_CHAR = #13 ; ASCII character code
;--------------;
; Main Program ;
;--------------;
start:
;-----------------------------------------------;
; Play sound effect - (API Group 8, Function 5) ;
;-----------------------------------------------;
lda neo.API_SOUND_CH_00 ; sound channel (API::sound->play->channel)
sta neo.API_PARAMETERS + 0 ; set API 'Parameter0' (API::sound->play->channel)
lda neo.API_SFX_COIN ; sound effect index (API::sound->play->effect)
sta neo.API_PARAMETERS + 1 ; set API 'Parameter1' (API::sound->play->effect)
lda neo.API_FN_PLAY_SOUND ; sound effect function (API::sound->play)
sta neo.API_FUNCTION ; set API 'Function' (API::sound->play)
lda neo.API_GROUP_SOUND ; 'Sound' API function group (API::sound)
sta neo.API_COMMAND ; trigger 'Sound' API routine (API::sound)
;--------------------------------------------------;
; Set cursor position - (API Group 2, Function 7) ;
;--------------------------------------------------;
; reposition the cursor to overwrite the default welcome text
lda neo.API_FN_SET_CURSOR_POS ; set cursor position function (API::console->cursor)
sta neo.API_FUNCTION ; set API 'Function' (API::console->cursor)
lda CURSOR_POS_X ; cursor 'X' coordinate (API::console->cursor->x)
sta neo.API_PARAMETERS + 0 ; set API 'Parameter0' (API::console->cursor->x)
lda CURSOR_POS_Y ; cursor 'Y' coordinate (API::console->cursor->y)
sta neo.API_PARAMETERS + 1 ; set API 'Parameter1' (API::console->cursor->y)
lda neo.API_GROUP_CONSOLE ; 'Console' API function group (API::console)
sta neo.API_COMMAND ; trigger 'Console' API routine (API::console)
; this simply repeats the same routine as the previous block,
; but using the generic convenience macro, for the sake of demonstration
lda CURSOR_POS_X
sta neo.API_PARAMETERS + 0
lda CURSOR_POS_Y
sta neo.API_PARAMETERS + 1
#neo.DoSendMessage ; send command 2,7
.byte 2,7
;--------------------------------------------------------;
; Write character to console - (API Group 2, Function 6) ;
;--------------------------------------------------------;
; first, write a single newline character, using the special convenience macro
lda NEWLINE_CHAR
jsr neo.WriteCharacter
; the text foreground color can also be set by injecting a control character
lda neo.COLOR_DARK_GREEN
jsr neo.WriteCharacter
; next, print the welcome message (a string of characters), using the API
ldx #0 ; initialize string iteration index
lda neo.API_FN_WRITE_CHAR ; console write function (API::console->write)
sta neo.API_FUNCTION ; set API 'Function' (API::console->write)
print_next_char:
lda neo.API_COMMAND ; previous API routine status
bne print_next_char ; wait for previous API routine to complete
lda hello_msg , x ; next character of 'hello_msg' (API::console->write->char)
beq end ; test for string end null byte
sta neo.API_PARAMETERS + 0 ; set API 'Parameter0' (API::console->write->char)
lda neo.API_GROUP_CONSOLE ; 'Console' API function group (API::console)
sta neo.API_COMMAND ; trigger 'Console' API routine (API::console)
inx ; increment iteration index
jmp print_next_char ; continue 'hello_msg' print loop
end:
jmp end ; infinite loop
;--------------;
; Program data ;
;--------------;
hello_msg:
.text " Hello Neo6502" ; line 1 to display
.text 13 ; newline
.text " " ; 53 blanks
.text 13 ; newline
.text " Now you're playing with Neo Power!" ; line 2 to display
.text 13 ; newline
.text " (Some assembly required)" ; line 3 to display
.text 0 ; null-terminated
}}
}
} }

View File

@@ -69,6 +69,8 @@ ALIGN64: '@align64' ;
ALIGNPAGE: '@alignpage' ; ALIGNPAGE: '@alignpage' ;
INITONCE: '@initonce' ;
ARRAYSIG : '[' [ \t]* ']' ; ARRAYSIG : '[' [ \t]* ']' ;
NOT_IN: 'not' [ \t]+ 'in' [ \t] ; NOT_IN: 'not' [ \t]+ 'in' [ \t] ;
@@ -157,7 +159,7 @@ directivearg : stringliteral | identifier | integerliteral ;
vardecl: datatype (arrayindex | ARRAYSIG)? decloptions identifier (',' identifier)* ; vardecl: datatype (arrayindex | ARRAYSIG)? decloptions identifier (',' identifier)* ;
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | SPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE)* ; decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | SPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE | INITONCE)* ;
varinitializer : vardecl '=' expression ; varinitializer : vardecl '=' expression ;

View File

@@ -12,7 +12,7 @@
<option name="HAS_STRING_ESCAPES" value="true" /> <option name="HAS_STRING_ESCAPES" value="true" />
</options> </options>
<keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" /> <keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" /> <keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@initonce;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords3 keywords="bool;byte;const;float;str;ubyte;uword;void;word" /> <keywords3 keywords="bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords4 keywords="abs;call;callfar;callfar2;clamp;cmp;defer;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" /> <keywords4 keywords="abs;call;callfar;callfar2;clamp;cmp;defer;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
</highlighting> </highlighting>

View File

@@ -43,7 +43,7 @@ syn region prog8ArrayType matchgroup=prog8Type
\ start="\<\%(u\?byte\|u\?word\|float\|str\|bool\)\[" end="\]" \ start="\<\%(u\?byte\|u\?word\|float\|str\|bool\)\[" end="\]"
\ transparent \ transparent
syn keyword prog8StorageClass const syn keyword prog8StorageClass const
syn match prog8StorageClass "\(^\|\s\)\(@zp\|@bank\|@shared\|@split\|@nozp\|@requirezp\|@align64\|@alignword\|@alignpage\)\>" syn match prog8StorageClass "\(^\|\s\)\(@zp\|@bank\|@shared\|@split\|@nozp\|@requirezp\|@align64\|@alignword\|@alignpage\|@initonce\)\>"
syn region prog8Block start="{" end="}" transparent syn region prog8Block start="{" end="}" transparent
syn region prog8Expression start="(" end=")" transparent syn region prog8Expression start="(" end=")" transparent