Merge branch 'v7.1' into testability_steps_1_2_3_again

# Conflicts:
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	compilerAst/src/prog8/parser/ModuleParsing.kt
#	compilerAst/test/TestAntlrParser.kt
#	parser/antlr/Prog8ANTLR.g4
This commit is contained in:
Irmen de Jong 2021-10-10 22:20:08 +02:00
commit f37fb82d53
58 changed files with 338 additions and 333 deletions

2
.idea/compiler.xml generated
View File

@ -4,4 +4,4 @@
<option name="BUILD_PROCESS_HEAP_SIZE" value="1200" /> <option name="BUILD_PROCESS_HEAP_SIZE" value="1200" />
<bytecodeTargetLevel target="11" /> <bytecodeTargetLevel target="11" />
</component> </component>
</project> </project>

View File

@ -1,3 +1,3 @@
plugins { plugins {
id "org.jetbrains.kotlin.jvm" version "1.5.20" apply false id "org.jetbrains.kotlin.jvm" version "1.5.30" apply false
} }

View File

@ -4,7 +4,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
%target c64
%option enable_floats %option enable_floats
floats { floats {

View File

@ -1,4 +1,3 @@
%target c64
%import textio %import textio
; bitmap pixel graphics module for the C64 ; bitmap pixel graphics module for the C64

View File

@ -5,8 +5,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
%target c64
c64 { c64 {
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte &ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte &ubyte TIME_MID = $a1 ; .. mid byte
@ -502,11 +500,9 @@ sys {
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling. ; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
; note: a more accurate way to wait for vsync is to set up a vsync irq handler instead. ; note: a more accurate way to wait for vsync is to set up a vsync irq handler instead.
%asm {{ %asm {{
- lda c64.RASTER - bit c64.SCROLY
beq - bpl -
- lda c64.RASTER - bit c64.SCROLY
bne -
bit c64.SCROLY
bmi - bmi -
rts rts
}} }}

View File

@ -4,7 +4,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
%target c64
%import syslib %import syslib
%import conv %import conv

View File

@ -4,7 +4,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
%target cx16
%option enable_floats %option enable_floats
floats { floats {

View File

@ -1,5 +1,3 @@
%target cx16
; Bitmap pixel graphics routines for the CommanderX16 ; Bitmap pixel graphics routines for the CommanderX16
; Custom routines to use the full-screen 640x480 and 320x240 screen modes. ; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
; (These modes are not supported by the documented GRAPH_xxxx kernal routines) ; (These modes are not supported by the documented GRAPH_xxxx kernal routines)

View File

@ -1,4 +1,3 @@
%target cx16
%import syslib %import syslib
%import textio %import textio

View File

@ -1,5 +1,3 @@
%target cx16
; Manipulate the Commander X16's display color palette. ; Manipulate the Commander X16's display color palette.
; Should you want to restore the default palette, you have to reinitialize the Vera yourself. ; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
@ -9,7 +7,7 @@ palette {
ubyte c ubyte c
sub set_color(ubyte index, uword color) { sub set_color(ubyte index, uword color) {
vera_palette_ptr = $fa00+index*2 vera_palette_ptr = $fa00+(index as uword * 2)
cx16.vpoke(1, vera_palette_ptr, lsb(color)) cx16.vpoke(1, vera_palette_ptr, lsb(color))
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(color)) cx16.vpoke(1, vera_palette_ptr, msb(color))
@ -70,11 +68,11 @@ palette {
} }
} }
inline sub set_all_black() { sub set_all_black() {
set_monochrome($000, $000) set_monochrome($000, $000)
} }
inline sub set_all_white() { sub set_all_white() {
set_monochrome($fff, $fff) set_monochrome($fff, $fff)
} }

View File

@ -5,9 +5,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
%target cx16
c64 { c64 {
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ---- ; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----

View File

@ -4,7 +4,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
%target cx16
%import syslib %import syslib
%import conv %import conv

View File

@ -301,17 +301,6 @@ private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions:
break break
} }
val inliner = SubroutineInliner(programAst, errors, options)
inliner.visit(programAst)
errors.report()
if(errors.noErrors()) {
inliner.applyModifications()
inliner.fixCallsToInlinedSubroutines()
val remover2 = UnusedCodeRemover(programAst, errors, compTarget)
remover2.visit(programAst)
remover2.applyModifications()
}
errors.report() errors.report()
} }

View File

@ -270,10 +270,11 @@ internal class AstChecker(private val program: Program,
} }
} }
// scope check if(subroutine.inline && !subroutine.isAsmSubroutine)
if(subroutine.parent !is Block && subroutine.parent !is Subroutine) { err("subroutine inlining is currently only supported on asmsub routines")
if(subroutine.parent !is Block && subroutine.parent !is Subroutine)
err("subroutines can only be defined in the scope of a block or within another subroutine") err("subroutines can only be defined in the scope of a block or within another subroutine")
}
if(subroutine.isAsmSubroutine) { if(subroutine.isAsmSubroutine) {
if(subroutine.asmParameterRegisters.size != subroutine.parameters.size) if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
@ -715,14 +716,6 @@ internal class AstChecker(private val program: Program,
else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page")}.any { !it }) else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page")}.any { !it })
err("invalid option directive argument(s)") err("invalid option directive argument(s)")
} }
"%target" -> {
if(directive.parent !is Block && directive.parent !is Module)
err("this directive may only occur in a block or at module level")
if(directive.args.size != 1)
err("directive requires one argument")
if(directive.args.single().name !in setOf(C64Target.name, Cx16Target.name))
err("invalid compilation target")
}
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position) else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
} }
super.visit(directive) super.visit(directive)

View File

@ -33,16 +33,6 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
super.visit(block) super.visit(block)
} }
override fun visit(directive: Directive) {
if(directive.directive=="%target") {
val compatibleTarget = directive.args.single().name
if (compatibleTarget != compTarget.name)
errors.err("module's compilation target ($compatibleTarget) differs from active target (${compTarget.name})", directive.position)
}
super.visit(directive)
}
override fun visit(decl: VarDecl) { override fun visit(decl: VarDecl) {
decl.datatypeErrors.forEach { errors.err(it.message, it.position) } decl.datatypeErrors.forEach { errors.err(it.message, it.position) }

View File

@ -864,7 +864,7 @@ internal class AsmGen(private val program: Program,
if(sub.inline) { if(sub.inline) {
if(options.optimize) { if(options.optimize) {
if(sub.isAsmSubroutine ||callGraph.unused(sub)) if(sub.isAsmSubroutine || callGraph.unused(sub))
return return
// from an inlined subroutine only the local variables are generated, // from an inlined subroutine only the local variables are generated,
@ -873,7 +873,7 @@ internal class AsmGen(private val program: Program,
onlyVariables = true onlyVariables = true
} }
else if(sub.amountOfRtsInAsm()==0) { else if(sub.amountOfRtsInAsm()==0) {
// make sure the NOT INLINED subroutine actually does an rts at the end // make sure the NOT INLINED subroutine actually does a rts at the end
sub.statements.add(Return(null, Position.DUMMY)) sub.statements.add(Return(null, Position.DUMMY))
} }
} }

View File

@ -750,7 +750,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// optimized simple case: swap two memory locations // optimized simple case: swap two memory locations
if(first is DirectMemoryRead && second is DirectMemoryRead) { if(first is DirectMemoryRead && second is DirectMemoryRead) {
// TODO optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+i1), @(ptr+i2))
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex() val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex() val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex()
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
@ -773,6 +772,50 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" ldy $name1 | lda $name2 | sta $name1 | sty $name2") asmgen.out(" ldy $name1 | lda $name2 | sta $name1 | sty $name2")
return return
} }
addr1==null && addr2==null && name1==null && name2==null -> {
val firstExpr = first.addressExpression as? BinaryExpression
val secondExpr = second.addressExpression as? BinaryExpression
if(firstExpr!=null && secondExpr!=null) {
val pointerVariable = firstExpr.left as? IdentifierReference
val firstOffset = firstExpr.right
val secondOffset = secondExpr.right
if(pointerVariable != null
&& pointerVariable isSameAs secondExpr.left
&& firstExpr.operator == "+" && secondExpr.operator == "+"
&& (firstOffset is NumericLiteralValue || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
&& (secondOffset is NumericLiteralValue || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
) {
val pointerVar = firstExpr.left as IdentifierReference
if(firstOffset is NumericLiteralValue && secondOffset is NumericLiteralValue) {
if(firstOffset!=secondOffset) {
swapArrayValues(
DataType.UBYTE,
asmgen.asmVariableName(pointerVariable), firstOffset,
asmgen.asmVariableName(pointerVariable), secondOffset
)
return
}
} else if(firstOffset is TypecastExpression && secondOffset is TypecastExpression) {
if(firstOffset.type in WordDatatypes && secondOffset.type in WordDatatypes) {
val firstOffsetVar = firstOffset.expression as? IdentifierReference
val secondOffsetVar = secondOffset.expression as? IdentifierReference
if(firstOffsetVar!=null && secondOffsetVar!=null) {
if(firstOffsetVar!=secondOffsetVar) {
swapArrayValues(
DataType.UBYTE,
asmgen.asmVariableName(pointerVariable), firstOffsetVar,
asmgen.asmVariableName(pointerVariable), secondOffsetVar
)
return
}
}
}
} else if(firstOffset is IdentifierReference || secondOffset is IdentifierReference) {
throw AssemblyError("expected a typecast-to-word for index variable at ${firstOffset.position} and/or ${secondOffset.position}")
}
}
}
}
} }
} }
@ -888,7 +931,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
lda #>(${arrayVarName2}+$index2) lda #>(${arrayVarName2}+$index2)
sta P8ZP_SCRATCH_W2+1 sta P8ZP_SCRATCH_W2+1
jsr floats.swap_floats jsr floats.func_swap_f
""") """)
} }
else -> throw AssemblyError("invalid aray elt type") else -> throw AssemblyError("invalid aray elt type")
@ -961,7 +1004,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
bcc + bcc +
inc P8ZP_SCRATCH_W2+1 inc P8ZP_SCRATCH_W2+1
+ jsr floats.swap_floats + jsr floats.func_swap_f
""") """)
} }
else -> throw AssemblyError("invalid aray elt type") else -> throw AssemblyError("invalid aray elt type")
@ -1019,7 +1062,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
bcc + bcc +
inc P8ZP_SCRATCH_W1+1 inc P8ZP_SCRATCH_W1+1
+ jsr floats.swap_floats + jsr floats.func_swap_f
""") """)
} }
else -> throw AssemblyError("invalid aray elt type") else -> throw AssemblyError("invalid aray elt type")
@ -1077,7 +1120,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
lda #>(${arrayVarName2}+$index2) lda #>(${arrayVarName2}+$index2)
sta P8ZP_SCRATCH_W2+1 sta P8ZP_SCRATCH_W2+1
jsr floats.swap_floats jsr floats.func_swap_f
""") """)
} }
else -> throw AssemblyError("invalid aray elt type") else -> throw AssemblyError("invalid aray elt type")

View File

@ -118,16 +118,14 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// we do this by copying the subroutine's statements at the call site. // we do this by copying the subroutine's statements at the call site.
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine // NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
// (this condition has been enforced by an ast check earlier) // (this condition has been enforced by an ast check earlier)
// note: for now, this is only reliably supported for asmsubs.
if(!sub.isAsmSubroutine)
throw AssemblyError("can only reliably inline asmsub routines at this time")
asmgen.out(" \t; inlined routine follows: ${sub.name}") asmgen.out(" \t; inlined routine follows: ${sub.name}")
val statements = sub.statements.filter { it !is ParameterVarDecl && it !is Directive } val assembly = sub.statements.single() as InlineAssembly
statements.forEach { asmgen.translate(assembly)
if(it is Return) {
asmgen.translate(it, false) // don't use RTS for the inlined return statement
} else {
if(!sub.inline || it !is VarDecl)
asmgen.translate(it)
}
}
asmgen.out(" \t; inlined routine end: ${sub.name}") asmgen.out(" \t; inlined routine end: ${sub.name}")
} }

View File

@ -1359,8 +1359,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) { internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
// we make an exception in the type check for assigning something to a cx16 virtual register // we make an exception in the type check for assigning something to a cx16 virtual register, or a register pair
if(target.register !in Cx16VirtualRegisters) { // these will be correctly typecasted from a byte to a word value
if(target.register !in Cx16VirtualRegisters &&
target.register!=RegisterOrPair.AX && target.register!=RegisterOrPair.AY && target.register!=RegisterOrPair.XY) {
if(target.kind==TargetStorageKind.VARIABLE) { if(target.kind==TargetStorageKind.VARIABLE) {
val parts = target.asmVarname.split('.') val parts = target.asmVarname.split('.')
if (parts.size != 2 || parts[0] != "cx16") if (parts.size != 2 || parts[0] != "cx16")

View File

@ -14,6 +14,7 @@ import prog8.compiler.astprocessing.toConstantIntegerRange
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
// Fix up the literal value's type to match that of the vardecl // Fix up the literal value's type to match that of the vardecl
// (also check range literal operands types before they get expanded to arrays for instance)
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() { internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
@ -54,6 +55,35 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
} }
return noModifications return noModifications
} }
override fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> {
val from = range.from.constValue(program)?.number?.toDouble()
val to = range.to.constValue(program)?.number?.toDouble()
val step = range.step.constValue(program)?.number?.toDouble()
if(from==null) {
if(!range.from.inferType(program).isInteger())
errors.err("range expression from value must be integer", range.from.position)
} else if(from-from.toInt()>0) {
errors.err("range expression from value must be integer", range.from.position)
}
if(to==null) {
if(!range.to.inferType(program).isInteger())
errors.err("range expression to value must be integer", range.to.position)
} else if(to-to.toInt()>0) {
errors.err("range expression to value must be integer", range.to.position)
}
if(step==null) {
if(!range.step.inferType(program).isInteger())
errors.err("range expression step value must be integer", range.step.position)
} else if(step-step.toInt()>0) {
errors.err("range expression step value must be integer", range.step.position)
}
return noModifications
}
} }

View File

@ -40,6 +40,41 @@ internal class StatementOptimizer(private val program: Program,
return noModifications return noModifications
} }
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
// if the first instruction in the called subroutine is a return statement with a simple value,
// remove the jump altogeter and inline the returnvalue directly.
val subroutine = functionCall.target.targetSubroutine(program)
if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Return && first.value?.isSimple==true) {
val orig = first.value!!
val copy = when(orig) {
is AddressOf -> {
val scoped = scopePrefix(orig.identifier, subroutine)
AddressOf(scoped, orig.position)
}
is DirectMemoryRead -> {
when(val expr = orig.addressExpression) {
is NumericLiteralValue -> DirectMemoryRead(expr.copy(), orig.position)
else -> return noModifications
}
}
is IdentifierReference -> scopePrefix(orig, subroutine)
is NumericLiteralValue -> orig.copy()
is StringLiteralValue -> orig.copy()
else -> return noModifications
}
return listOf(IAstModification.ReplaceNode(functionCall, copy, parent))
}
}
return noModifications
}
private fun scopePrefix(variable: IdentifierReference, subroutine: Subroutine): IdentifierReference {
val scoped = subroutine.makeScopedName(variable.nameInSource.last())
return IdentifierReference(scoped.split('.'), variable.position)
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in functions.names) { if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in functions.names) {
val functionName = functionCallStatement.target.nameInSource[0] val functionName = functionCallStatement.target.nameInSource[0]
@ -102,19 +137,19 @@ internal class StatementOptimizer(private val program: Program,
return noModifications return noModifications
} }
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> { // override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
// if the first instruction in the called subroutine is a return statement with constant value, replace with the constant value // // if the first instruction in the called subroutine is a return statement with constant value, replace with the constant value
val subroutine = functionCall.target.targetSubroutine(program) // val subroutine = functionCall.target.targetSubroutine(program)
if(subroutine!=null) { // if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull() // val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Return && first.value!=null) { // if(first is Return && first.value!=null) {
val constval = first.value?.constValue(program) // val constval = first.value?.constValue(program)
if(constval!=null) // if(constval!=null)
return listOf(IAstModification.ReplaceNode(functionCall, constval, parent)) // return listOf(IAstModification.ReplaceNode(functionCall, constval, parent))
} // }
} // }
return noModifications // return noModifications
} // }
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> { override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
// remove empty if statements // remove empty if statements

View File

@ -1,96 +0,0 @@
package prog8.optimizer
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.Position
import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.CompilationOptions
import prog8.compiler.IErrorReporter
internal class SubroutineInliner(private val program: Program, val errors: IErrorReporter, private val compilerOptions: CompilationOptions): AstWalker() {
private var callsToInlinedSubroutines = mutableListOf<Pair<IFunctionCall, Node>>()
fun fixCallsToInlinedSubroutines() {
for((call, parent) in callsToInlinedSubroutines) {
val sub = call.target.targetSubroutine(program)!!
val intermediateReturnValueVar = sub.statements.filterIsInstance<VarDecl>().singleOrNull { it.name.endsWith(retvarName) }
if(intermediateReturnValueVar!=null) {
val scope = parent.definingScope()
if(!scope.statements.filterIsInstance<VarDecl>().any { it.name==intermediateReturnValueVar.name}) {
val decl = intermediateReturnValueVar.copy()
scope.statements.add(0, decl)
decl.linkParents(scope as Node)
}
}
}
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
return if(compilerOptions.optimize && subroutine.inline && !subroutine.isAsmSubroutine)
annotateInlinedSubroutineIdentifiers(subroutine)
else
noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
return after(functionCallStatement as IFunctionCall, parent, functionCallStatement.position)
}
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
return after(functionCall as IFunctionCall, parent, functionCall.position)
}
private fun after(functionCall: IFunctionCall, parent: Node, position: Position): Iterable<IAstModification> {
val sub = functionCall.target.targetSubroutine(program)
if(sub != null && compilerOptions.optimize && sub.inline && !sub.isAsmSubroutine)
callsToInlinedSubroutines.add(Pair(functionCall, parent))
return noModifications
}
private fun annotateInlinedSubroutineIdentifiers(sub: Subroutine): List<IAstModification> {
// this adds name prefixes to the identifiers used in the subroutine,
// so that the statements can be inlined (=copied) in the call site and still reference
// the correct symbols as seen from the scope of the subroutine.
class Annotator: AstWalker() {
var numReturns=0
override fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
val stmt = identifier.targetStatement(program)!!
if(stmt is BuiltinFunctionStatementPlaceholder)
return noModifications
val prefixed = stmt.makeScopedName(identifier.nameInSource.last()).split('.')
val withPrefix = IdentifierReference(prefixed, identifier.position)
return listOf(IAstModification.ReplaceNode(identifier, withPrefix, parent))
}
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
numReturns++
if(parent !== sub || sub.indexOfChild(returnStmt)<sub.statements.size-1)
errors.err("return statement must be the very last statement in the inlined subroutine", sub.position)
return noModifications
}
fun theModifications(): List<IAstModification> {
return this.modifications.map { it.first }.toList()
}
}
val annotator = Annotator()
sub.accept(annotator, sub.parent)
if(annotator.numReturns>1) {
errors.err("inlined subroutine can only have one return statement", sub.position)
return noModifications
}
return annotator.theModifications()
}
}

View File

@ -12,6 +12,14 @@ import prog8.parser.Prog8ANTLRParser
private data class NumericLiteral(val number: Number, val datatype: DataType) private data class NumericLiteral(val number: Number, val datatype: DataType)
// TODO [merge conflict]: not sure if this should be kept?? Is it double??
internal fun Prog8ANTLRParser.ModuleContext.toAst(name: String, source: Path, encoding: IStringEncoding) : Module {
val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name
val directives = this.directive().map { it.toAst() }
val blocks = this.block().map { it.toAst(Module.isLibrary(source), encoding) }
return Module(nameWithoutSuffix, (directives + blocks).toMutableList(), toPosition(), source)
}
private fun ParserRuleContext.toPosition() : Position { private fun ParserRuleContext.toPosition() : Position {
/* /*
val customTokensource = this.start.tokenSource as? CustomLexer val customTokensource = this.start.tokenSource as? CustomLexer

View File

@ -347,7 +347,7 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
this.addressExpression.linkParents(this) this.addressExpression.linkParents(this)
} }
override val isSimple = true override val isSimple = addressExpression is NumericLiteralValue || addressExpression is IdentifierReference
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Expression && node===addressExpression) require(replacement is Expression && node===addressExpression)
@ -365,8 +365,6 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
override fun toString(): String { override fun toString(): String {
return "DirectMemoryRead($addressExpression)" return "DirectMemoryRead($addressExpression)"
} }
fun copy() = DirectMemoryRead(addressExpression, position)
} }
class NumericLiteralValue(val type: DataType, // only numerical types allowed class NumericLiteralValue(val type: DataType, // only numerical types allowed
@ -375,6 +373,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
override lateinit var parent: Node override lateinit var parent: Node
override val isSimple = true override val isSimple = true
fun copy() = NumericLiteralValue(type, number, position)
companion object { companion object {
fun fromBoolean(bool: Boolean, position: Position) = fun fromBoolean(bool: Boolean, position: Position) =
@ -539,6 +538,7 @@ class StringLiteralValue(val value: String,
} }
override val isSimple = true override val isSimple = true
fun copy() = StringLiteralValue(value, altEncoding, position)
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
throw FatalAstException("can't replace here") throw FatalAstException("can't replace here")

View File

@ -641,9 +641,13 @@ Subroutines can be defined in a Block, but also nested inside another subroutine
With ``asmsub`` you can define a low-level subroutine that is implemented in inline assembly and takes any parameters With ``asmsub`` you can define a low-level subroutine that is implemented in inline assembly and takes any parameters
in registers directly. in registers directly.
Trivial subroutines can be tagged as ``inline`` to tell the compiler to copy their code Trivial ``asmsub`` routines can be tagged as ``inline`` to tell the compiler to copy their code
in-place to the locations where the subroutine is called, rather than inserting an actual call and return to the in-place to the locations where the subroutine is called, rather than inserting an actual call and return to the
subroutine. This may increase code size significantly and can only be used in limited scenarios, so YMMV. subroutine. This may increase code size significantly and can only be used in limited scenarios, so YMMV.
Note that the routine's code is copied verbatim into the place of the subroutine call in this case,
so pay attention to any jumps and rts instructions in the inlined code!
At this time it is not yet possible to inline regular Prog8 subroutines, this may be added in the future.
Calling a subroutine Calling a subroutine

View File

@ -33,13 +33,6 @@ This makes it easier to understand and relate the generated code. Examples::
Directives Directives
----------- -----------
.. data:: %target <target>
Level: module.
Global setting, specifies that this module can only work for the given compiler target.
If compiled with a different target, compilation is aborted with an error message.
.. data:: %output <type> .. data:: %output <type>
Level: module. Level: module.
@ -403,7 +396,7 @@ The following names are reserved, they have a special meaning::
Range expression Range expression
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
A special value is the *range expression* which represents a range of numbers or characters, A special value is the *range expression* which represents a range of integer numbers or characters,
from the starting value to (and including) the ending value:: from the starting value to (and including) the ending value::
<start> to <end> [ step <step> ] <start> to <end> [ step <step> ]
@ -413,7 +406,7 @@ You an provide a step value if you need something else than the default incremen
in case of downto, a decrement of one). Because a step of minus one is so common you can just use in case of downto, a decrement of one). Because a step of minus one is so common you can just use
the downto variant to avoid having to specify the step as well. the downto variant to avoid having to specify the step as well.
If used in the place of a literal value, it expands into the actual array of values:: If used in the place of a literal value, it expands into the actual array of integer values::
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199] byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
@ -559,7 +552,7 @@ Subroutine definitions
The syntax is:: The syntax is::
[inline] sub <identifier> ( [parameters] ) [ -> returntype ] { sub <identifier> ( [parameters] ) [ -> returntype ] {
... statements ... ... statements ...
} }
@ -572,9 +565,6 @@ The open curly brace must immediately follow the subroutine result specification
and can have nothing following it. The close curly brace must be on its own line as well. and can have nothing following it. The close curly brace must be on its own line as well.
The parameters is a (possibly empty) comma separated list of "<datatype> <parametername>" pairs specifying the input parameters. The parameters is a (possibly empty) comma separated list of "<datatype> <parametername>" pairs specifying the input parameters.
The return type has to be specified if the subroutine returns a value. The return type has to be specified if the subroutine returns a value.
The ``inline`` keyword makes their code copied in-place to the locations where the subroutine is called,
rather than having an actual call and return to the subroutine. This is meant for very small subroutines only
as it can increase code size significantly.
Assembly / ROM subroutines Assembly / ROM subroutines

View File

@ -1,4 +1,3 @@
%target c64
%import syslib %import syslib
%import textio %import textio
%import test_stack %import test_stack

View File

@ -1,4 +1,3 @@
%target c64
%import syslib %import syslib
%import textio %import textio
%zeropage basicsafe %zeropage basicsafe

View File

@ -1,4 +1,3 @@
%target c64
%import textio %import textio
%import syslib %import syslib

View File

@ -1,4 +1,3 @@
%target c64
%import syslib %import syslib
%import textio %import textio
%zeropage basicsafe %zeropage basicsafe

View File

@ -1,4 +1,3 @@
%target c64
%import syslib %import syslib
%import textio %import textio
%import test_stack %import test_stack

View File

@ -1,4 +1,3 @@
%target c64
%import syslib %import syslib
%import test_stack %import test_stack
%import textio %import textio

View File

@ -1,4 +1,3 @@
%target cx16
%import textio %import textio
%import palette %import palette
%import string %import string

View File

@ -1,4 +1,3 @@
%target cx16
%import palette %import palette
%import conv %import conv
%import textio %import textio

View File

@ -1,4 +1,3 @@
%target cx16
%import syslib %import syslib
%import test_stack %import test_stack
%import conv %import conv

View File

@ -1,27 +1,24 @@
%import gfx2
%import palette
%import textio %import textio
; Amiga 'copper' bars color cycling effect
; TODO WORK IN PROGRESS...
; want to make Amiga 'copper' bars color cycling effects
main { main {
sub start() { sub start() {
palette.set_all_black() ; make palette color 1 black so we can print black letters over the background color 0
gfx2.screen_mode(4) void cx16.screen_set_mode(0)
cx16.vpoke(1, $fa02, $0)
cx16.vpoke(1, $fa03, $0)
txt.color(1)
txt.plot(13,12)
txt.print("amiga-inspired")
txt.plot(10,14)
txt.print("raster blinds effect")
txt.plot(12,16)
txt.print("random gradients")
ubyte yy irq.make_new_gradient()
for yy in 0 to 239 cx16.set_rasterirq(&irq.irqhandler, irq.top_scanline)
gfx2.horizontal_line(0, yy, 320, yy & 63)
repeat {
colors.random_bar()
colors.set_palette()
repeat 20
sys.waitvsync()
}
repeat { repeat {
} }
@ -29,99 +26,165 @@ main {
} }
irq {
const ubyte top_scanline = 0
ubyte blinds_start_ix = 0
ubyte color_ix = 0
uword next_irq_line = top_scanline
ubyte shift_counter = 0
ubyte[32+32+16] blinds_lines_reds
ubyte[32+32+16] blinds_lines_greens
ubyte[32+32+16] blinds_lines_blues
sub irqhandler() {
set_scanline_color(color_ix)
color_ix++
next_irq_line += 2 ; code needs 2 scanlines per color transition
if next_irq_line == 480 {
; start over at top
next_irq_line = top_scanline
blinds_start_ix = 0
color_ix = 0
shift_counter++
if shift_counter == 32+32+32 {
make_new_gradient()
shift_counter = 0
} else if shift_counter & 1 {
shift_gradient()
}
} else if next_irq_line & 15 == 0 {
; start next blinds
blinds_start_ix++
color_ix = blinds_start_ix
}
cx16.set_rasterline(next_irq_line)
}
sub make_new_gradient() {
colors.random_half_bar()
colors.mirror_bar()
sys.memcopy(colors.reds, &blinds_lines_reds+32+16, len(colors.reds))
sys.memcopy(colors.greens, &blinds_lines_greens+32+16, len(colors.greens))
sys.memcopy(colors.blues, &blinds_lines_blues+32+16, len(colors.blues))
}
sub shift_gradient() {
sys.memcopy(&blinds_lines_reds+1, blinds_lines_reds, len(blinds_lines_reds)-1)
sys.memcopy(&blinds_lines_greens+1, blinds_lines_greens, len(blinds_lines_greens)-1)
sys.memcopy(&blinds_lines_blues+1, blinds_lines_blues, len(blinds_lines_blues)-1)
}
asmsub set_scanline_color(ubyte color_ix @Y) {
; uword color = mkword(reds[ix], (greens[ix] << 4) | blues[ix] )
%asm {{
lda blinds_lines_reds,y
pha
lda blinds_lines_greens,y
asl a
asl a
asl a
asl a
ora blinds_lines_blues,y
tay
stz cx16.VERA_CTRL
lda #%00010001
sta cx16.VERA_ADDR_H
lda #$fa
sta cx16.VERA_ADDR_M
; lda #$02
; sta cx16.VERA_ADDR_L
stz cx16.VERA_ADDR_L
sty cx16.VERA_DATA0 ; gb
pla
sta cx16.VERA_DATA0 ; r
stz cx16.VERA_ADDR_H
rts
}}
}
}
colors { colors {
ubyte cr ubyte target_red
ubyte cg ubyte target_green
ubyte cb ubyte target_blue
ubyte[48+16] reds ubyte[32] reds
ubyte[48+16] greens ubyte[32] greens
ubyte[48+16] blues ubyte[32] blues
ubyte bar_size
sub random_rgb12() { sub random_rgb12() {
do { do {
uword rr = rndw() uword rr = rndw()
cr = msb(rr) & 15 target_red = msb(rr) & 15
cg = lsb(rr) target_green = lsb(rr)
cb = cg & 15 target_blue = target_green & 15
cg >>= 4 target_green >>= 4
} until cr+cg+cb >= 12 } until target_red+target_green+target_blue >= 12
} }
sub random_bar() { sub mirror_bar() {
; mirror the top half bar into the bottom half
ubyte ix=14
ubyte mix=16
do {
reds[mix] = reds[ix]
greens[mix] = greens[ix]
blues[mix] = blues[ix]
mix++
ix--
} until ix==255
reds[mix] = 0
greens[mix] = 0
blues[mix] = 0
}
sub random_half_bar() {
; fade black -> color then fade color -> white ; fade black -> color then fade color -> white
; gradient calculations in 8.8 bits fixed-point
; could theoretically be 4.12 bits for even more fractional accuracy
random_rgb12() random_rgb12()
ubyte r=0 uword r = $000
ubyte g=0 uword g = $000
ubyte b=0 uword b = $000
ubyte different uword dr = target_red
bar_size = 0 uword dg = target_green
uword db = target_blue
ubyte ix = 1
repeat { ; gradient from black to halfway color
different = false reds[0] = 0
if r != cr { greens[0] = 0
different = true blues[0] = 0
r++ dr <<= 5
} dg <<= 5
if g != cg { db <<= 5
different = true continue_gradient()
g++
}
if b != cb {
different = true
b++
}
if not different
break
reds[bar_size] = r
greens[bar_size] = g
blues[bar_size] = b
bar_size++
}
repeat {
different = false
if r != 15 {
different = true
r++
}
if g != 15 {
different = true
g++
}
if b != 15 {
different = true
b++
}
if not different
break
reds[bar_size] = r
greens[bar_size] = g
blues[bar_size] = b
bar_size++
}
; mirror bottom half from top half
ubyte mi = bar_size-1
repeat mi {
reds[bar_size] = reds[mi]
greens[bar_size] = greens[mi]
blues[bar_size] = blues[mi]
bar_size++
mi--
}
; make rest of bar black (bars are not always the same length using the simplistic algorithm above...)
while bar_size != 48+16 {
reds[bar_size] = $0
greens[bar_size] = $0
blues[bar_size] = $0
bar_size++
}
}
sub set_palette() { ; gradient from halfway color to white
ubyte ix dr = (($f00 - r) >> 3) - 1
for ix in 0 to 48+15 { dg = (($f00 - g) >> 3) - 1
uword color = mkword(reds[ix], (greens[ix] << 4) | blues[ix] ) db = (($f00 - b) >> 3) - 1
palette.set_color(ix, color) continue_gradient()
return
sub continue_gradient() {
repeat 8 {
reds[ix] = msb(r)
greens[ix] = msb(g)
blues[ix] = msb(b)
r += dr
g += dg
b += db
ix++
}
} }
} }
} }

View File

@ -1,4 +1,3 @@
%target cx16
%import textio %import textio
main { main {

View File

@ -1,6 +1,5 @@
; CommanderX16 text datetime example! ; CommanderX16 text datetime example!
%target cx16
%import textio %import textio
%zeropage basicsafe %zeropage basicsafe

View File

@ -1,4 +1,3 @@
%target cx16
%import gfx2 %import gfx2
%import floats %import floats
%import textio %import textio

View File

@ -1,4 +1,3 @@
%target cx16
%import palette %import palette
%option no_sysinit %option no_sysinit

View File

@ -1,4 +1,3 @@
%target cx16
%import textio %import textio
%import floats %import floats
%zeropage basicsafe %zeropage basicsafe

View File

@ -1,4 +1,3 @@
%target cx16
%import textio %import textio
%import floats %import floats
%zeropage basicsafe %zeropage basicsafe

View File

@ -1,4 +1,3 @@
%target cx16
%import palette %import palette
%import gfx2 %import gfx2
%option no_sysinit %option no_sysinit

View File

@ -1,4 +1,3 @@
%target cx16
%import textio %import textio
%import palette %import palette

View File

@ -8,7 +8,6 @@
; simplistic sound effects (Vera PSG) ; simplistic sound effects (Vera PSG)
%target cx16
%import syslib %import syslib
%import textio %import textio
%import test_stack %import test_stack

View File

@ -1,4 +1,3 @@
%target cx16
%import gfx2 %import gfx2
%import textio %import textio
%import test_stack %import test_stack

View File

@ -1,4 +1,3 @@
%target cx16
%import textio %import textio
%option no_sysinit %option no_sysinit
%zeropage basicsafe %zeropage basicsafe

View File

@ -1,4 +1,3 @@
%target c64
%import syslib %import syslib
%import test_stack %import test_stack
%import textio %import textio

View File

@ -1,4 +1,3 @@
%target c64
%import syslib %import syslib
main { main {

View File

@ -1,4 +1,3 @@
%target c64
%import textio %import textio
%import syslib %import syslib
%zeropage basicsafe %zeropage basicsafe

View File

@ -8,7 +8,6 @@
; some simple sound effects ; some simple sound effects
%target c64
%import syslib %import syslib
%import textio %import textio
%import test_stack %import test_stack

View File

@ -1,4 +1,3 @@
%target c64
%import floats %import floats
%import graphics %import graphics
%import test_stack %import test_stack

View File

@ -1,4 +1,3 @@
%target c64
%import syslib %import syslib
%zeropage basicsafe %zeropage basicsafe

View File

@ -8,7 +8,7 @@ NOTES:
*/ */
// -> java classes Prog8ANTLRParser and Prog8ANTLRLexer, // -> java classes Prog8ANTLRParser and Prog8ANTLRLexer,
// both NOT to be used from Kotlin code, but ONLY through Kotlin class Prog8Parser // both NOT to be used from Kotlin code, but ONLY through Kotlin class Prog8Parser
grammar Prog8ANTLR; grammar Prog8ANTLR;
@ -137,7 +137,7 @@ unconditionaljump : 'goto' (integerliteral | scoped_identifier) ;
directive : directive :
directivename=('%output' | '%launcher' | '%zeropage' | '%zpreserved' | '%address' | '%import' | directivename=('%output' | '%launcher' | '%zeropage' | '%zpreserved' | '%address' | '%import' |
'%breakpoint' | '%asminclude' | '%asmbinary' | '%option' | '%target' ) '%breakpoint' | '%asminclude' | '%asmbinary' | '%option' )
(directivearg? | directivearg (',' directivearg)*) (directivearg? | directivearg (',' directivearg)*)
; ;

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;do;downto;else;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;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" /> <keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;do;downto;else;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;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" /> <keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%zeropage;%zpreserved" />
<keywords3 keywords="byte;const;float;shared;str;ubyte;uword;void;word;zp" /> <keywords3 keywords="byte;const;float;shared;str;ubyte;uword;void;word;zp" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;callfar;callrom;ceil;cmp;cos;cos16;cos16u;cos8;cos8u;deg;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;peek;peekw;poke;pokew;rad;reverse;rnd;rndf;rndw;rol;rol2;ror;ror2;round;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;sum;swap;tan" /> <keywords4 keywords="abs;acos;all;any;asin;atan;avg;callfar;callrom;ceil;cmp;cos;cos16;cos16u;cos8;cos8u;deg;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;peek;peekw;poke;pokew;rad;reverse;rnd;rndf;rndw;rol;rol2;ror;ror2;round;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;sum;swap;tan" />
</highlighting> </highlighting>
@ -20,4 +20,4 @@
<mapping ext="p8" /> <mapping ext="p8" />
<mapping ext="prog8" /> <mapping ext="prog8" />
</extensionMap> </extensionMap>
</filetype> </filetype>

View File

@ -25,7 +25,7 @@
<Keywords name="Folders in comment, middle"></Keywords> <Keywords name="Folders in comment, middle"></Keywords>
<Keywords name="Folders in comment, close"></Keywords> <Keywords name="Folders in comment, close"></Keywords>
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared</Keywords> <Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%target&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved</Keywords> <Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved</Keywords>
<Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat&#x000D;&#x000A;break return goto</Keywords> <Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat&#x000D;&#x000A;break return goto</Keywords>
<Keywords name="Keywords4">abs acos all any asin atan avg callfar callrom ceil cmp cos cos16 cos16u cos8 cos8u deg floor len ln log2 lsb lsl lsr max memory min mkword msb peek peekw poke pokew rad reverse rnd rndf rndw rol rol2 ror ror2 round sgn sin sin16 sin16u sin8 sin8u sizeof sort sqrt sqrt16 sum swap tan&#x000D;&#x000A;</Keywords> <Keywords name="Keywords4">abs acos all any asin atan avg callfar callrom ceil cmp cos cos16 cos16u cos8 cos8u deg floor len ln log2 lsb lsl lsr max memory min mkword msb peek peekw poke pokew rad reverse rnd rndf rndw rol rol2 ror ror2 round sgn sin sin16 sin16u sin8 sin8u sizeof sort sqrt sqrt16 sum swap tan&#x000D;&#x000A;</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto</Keywords> <Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto</Keywords>

View File

@ -1,4 +1,3 @@
%target cx16
%import textio %import textio
%zeropage basicsafe %zeropage basicsafe
%option no_sysinit %option no_sysinit

View File

@ -33,7 +33,7 @@ syn keyword prog8Repeat for while in do until repeat
syn match prog8Label "\<\w\+\>:" syn match prog8Label "\<\w\+\>:"
syn keyword prog8Operator and or to downto as void syn keyword prog8Operator and or to downto as void
syn match prog8Directive "\(^\|\s\)%\(target\|output\|launcher\|zeropage\)\>" syn match prog8Directive "\(^\|\s\)%\(output\|launcher\|zeropage\)\>"
syn match prog8Directive "\(^\|\s\)%\(zpreserved\|address\|import\|option\)\>" syn match prog8Directive "\(^\|\s\)%\(zpreserved\|address\|import\|option\)\>"
syn match prog8Directive "\(^\|\s\)%\(asmbinary\|asminclude\|breakpoint\)\>" syn match prog8Directive "\(^\|\s\)%\(asmbinary\|asminclude\|breakpoint\)\>"
syn match prog8Directive "\(^\|\s\)%asm\>" syn match prog8Directive "\(^\|\s\)%asm\>"