mirror of
https://github.com/irmen/prog8.git
synced 2025-02-08 16:30:28 +00:00
Merge branch 'initonce-var-tag'
# Conflicts: # examples/test.p8
This commit is contained in:
commit
93a0a41e73
@ -76,7 +76,7 @@ class VarConstantValueTypeAdjuster(
|
||||
if (declValue != null) {
|
||||
// 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)
|
||||
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
|
||||
return listOf(
|
||||
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
|
||||
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(
|
||||
IAstModification.ReplaceNode(decl, const, parent),
|
||||
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
|
||||
@ -398,7 +398,7 @@ internal class ConstantIdentifierReplacer(
|
||||
if(targetDatatype.isArray) {
|
||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOr(DataType.UNDEFINED),
|
||||
ZeropageWish.DONTCARE, null, "dummy", emptyList(),
|
||||
assignment.value, false, false, 0u, Position.DUMMY)
|
||||
assignment.value, false, false, 0u, false, Position.DUMMY)
|
||||
val replaceValue = createConstArrayInitializerValue(decl)
|
||||
if(replaceValue!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, replaceValue, assignment))
|
||||
@ -419,6 +419,21 @@ internal class ConstantIdentifierReplacer(
|
||||
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
|
||||
|
||||
// convert the initializer range expression from a range, to an actual array literal.
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -206,7 +206,7 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
val newDecl = VarDecl(
|
||||
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))
|
||||
}
|
||||
|
@ -26,8 +26,10 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
}
|
||||
|
||||
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))
|
||||
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
||||
if (decl.type == VarDeclType.VAR && decl.value != null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL)) {
|
||||
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
|
||||
}
|
||||
|
@ -528,6 +528,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
when(srcVar.type) {
|
||||
VarDeclType.VAR -> {
|
||||
val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null
|
||||
if(srcVar.initOnce && value==null)
|
||||
throw FatalAstException("initonce without value $srcVar")
|
||||
return PtVariable(
|
||||
srcVar.name,
|
||||
srcVar.datatype,
|
||||
|
@ -180,7 +180,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
}
|
||||
return VarDecl(
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,12 @@ internal class StatementReorderer(
|
||||
declsProcessedWithInitAssignment.add(decl)
|
||||
if (decl.value == null) {
|
||||
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,
|
||||
// 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
|
||||
@ -66,6 +72,9 @@ internal class StatementReorderer(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(decl.initOnce) {
|
||||
return noModifications
|
||||
}
|
||||
// Transform the vardecl with initvalue to a plain vardecl + assignment
|
||||
// this allows for other optimizations to kick in.
|
||||
// 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.splitArray,
|
||||
it.alignment,
|
||||
it.initOnce,
|
||||
it.position
|
||||
)
|
||||
IAstModification.ReplaceNode(it, newvar, subroutine)
|
||||
|
@ -114,7 +114,7 @@ class TestMemory: FunSpec({
|
||||
}
|
||||
|
||||
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 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)
|
||||
@ -152,7 +152,7 @@ class TestMemory: FunSpec({
|
||||
}
|
||||
|
||||
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 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)
|
||||
@ -164,7 +164,7 @@ class TestMemory: FunSpec({
|
||||
|
||||
test("memory mapped variable not in mapped IO ram on C64") {
|
||||
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 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)
|
||||
@ -176,7 +176,7 @@ class TestMemory: FunSpec({
|
||||
|
||||
test("memory mapped variable in mapped IO ram on C64") {
|
||||
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 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)
|
||||
@ -187,7 +187,7 @@ class TestMemory: FunSpec({
|
||||
}
|
||||
|
||||
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 target = AssignTarget(null, arrayindexed, null, null, false, 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") {
|
||||
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 target = AssignTarget(null, arrayindexed, null, null, false, 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") {
|
||||
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 target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||
|
@ -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 var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, 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, false, Position.DUMMY)
|
||||
val labelInSub = Label("locallabel", 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 subroutine = Subroutine("start", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, 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 module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
|
||||
|
@ -87,7 +87,7 @@ class Program(val name: String,
|
||||
val varName = "string_${internedStringsBlock.statements.size}"
|
||||
val decl = VarDecl(
|
||||
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)
|
||||
decl.linkParents(internedStringsBlock)
|
||||
|
@ -328,6 +328,8 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||
val options = it.decloptions()
|
||||
if(options.ALIGNPAGE().isNotEmpty() || options.ALIGNWORD().isNotEmpty())
|
||||
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)
|
||||
var datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||
if(it.ARRAYSIG()!=null || it.arrayindex()!=null)
|
||||
@ -769,6 +771,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
|
||||
options.SHARED().isNotEmpty(),
|
||||
split,
|
||||
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
|
||||
dt==DataType.STR || options.INITONCE().isNotEmpty(),
|
||||
toPosition()
|
||||
)
|
||||
}
|
||||
|
@ -249,6 +249,7 @@ class VarDecl(val type: VarDeclType,
|
||||
val sharedWithAsm: Boolean,
|
||||
val splitArray: Boolean,
|
||||
val alignment: UInt,
|
||||
val initOnce: Boolean,
|
||||
override val position: Position) : Statement(), INamedStatement {
|
||||
override lateinit var parent: Node
|
||||
var allowInitializeWithZero = true
|
||||
@ -262,6 +263,7 @@ class VarDecl(val type: VarDeclType,
|
||||
sharedWithAsm = false,
|
||||
splitArray = false,
|
||||
alignment = 0u,
|
||||
initOnce = false,
|
||||
position = param.position
|
||||
)
|
||||
}
|
||||
@ -271,10 +273,15 @@ class VarDecl(val type: VarDeclType,
|
||||
val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
|
||||
val arraysize = ArrayIndex.forArray(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 {
|
||||
if(datatype in SplitWordArrayTypes)
|
||||
require(splitArray)
|
||||
@ -313,7 +320,7 @@ class VarDecl(val type: VarDeclType,
|
||||
if(names.size>1)
|
||||
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(),
|
||||
sharedWithAsm, splitArray, alignment, position)
|
||||
sharedWithAsm, splitArray, alignment, initOnce, position)
|
||||
copy.allowInitializeWithZero = this.allowInitializeWithZero
|
||||
return copy
|
||||
}
|
||||
@ -328,19 +335,19 @@ class VarDecl(val type: VarDeclType,
|
||||
// just copy the initialization value to a separate vardecl for each component
|
||||
return names.map {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
// 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(),
|
||||
sharedWithAsm, splitArray, alignment, position)
|
||||
sharedWithAsm, splitArray, alignment, initOnce, position)
|
||||
first.allowInitializeWithZero = this.allowInitializeWithZero
|
||||
val firstVar = firstVarAsValue(first)
|
||||
return listOf(first) + names.drop(1 ).map {
|
||||
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
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
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.
|
||||
|
||||
@ -36,7 +40,7 @@ Future Things and Ideas
|
||||
- (What, how, isn't current BSS support enough?)
|
||||
- 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 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 .
|
||||
- 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?
|
||||
|
152
examples/test.p8
152
examples/test.p8
@ -1,118 +1,50 @@
|
||||
%output raw
|
||||
%launcher none
|
||||
%import textio
|
||||
%option no_sysinit
|
||||
%zeropage dontuse
|
||||
|
||||
main {
|
||||
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 :-)"
|
||||
WriteCharacter(cx16.r0L)
|
||||
dump()
|
||||
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
|
||||
|
||||
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,8 @@ ALIGN64: '@align64' ;
|
||||
|
||||
ALIGNPAGE: '@alignpage' ;
|
||||
|
||||
INITONCE: '@initonce' ;
|
||||
|
||||
ARRAYSIG : '[' [ \t]* ']' ;
|
||||
|
||||
NOT_IN: 'not' [ \t]+ 'in' [ \t] ;
|
||||
@ -157,7 +159,7 @@ directivearg : stringliteral | identifier | integerliteral ;
|
||||
|
||||
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 ;
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
<option name="HAS_STRING_ESCAPES" value="true" />
|
||||
</options>
|
||||
<keywords keywords="&;->;@;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" />
|
||||
<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>
|
||||
|
@ -43,7 +43,7 @@ syn region prog8ArrayType matchgroup=prog8Type
|
||||
\ start="\<\%(u\?byte\|u\?word\|float\|str\|bool\)\[" end="\]"
|
||||
\ transparent
|
||||
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 prog8Expression start="(" end=")" transparent
|
||||
|
Loading…
x
Reference in New Issue
Block a user