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
commit 93a0a41e73
17 changed files with 126 additions and 137 deletions

View File

@ -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.

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)
}

View File

@ -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))
}

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> {
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
}

View File

@ -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,

View File

@ -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
)
}

View File

@ -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)

View File

@ -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)

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 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"))

View File

@ -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)

View File

@ -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()
)
}

View File

@ -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
}

View File

@ -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?

View File

@ -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
}}
}
}

View File

@ -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 ;

View File

@ -12,7 +12,7 @@
<option name="HAS_STRING_ESCAPES" value="true" />
</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" />
<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>

View File

@ -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