changed @initonce to @dirty and meaning is now: not initialized at all.

This commit is contained in:
Irmen de Jong 2024-11-08 22:05:31 +01:00
parent 3ee6058524
commit 64164c1c72
16 changed files with 105 additions and 112 deletions

View File

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

View File

@ -920,14 +920,14 @@ internal class AstChecker(private val program: Program,
} }
if(decl.datatype==DataType.STR) { if (decl.dirty) {
if(!decl.initOnce) if(decl.datatype==DataType.STR)
throw FatalAstException("string vars must be initonce") errors.err("string variables cannot be @dirty", decl.position)
} else {
if(decl.value==null)
if (decl.initOnce) { errors.info("dirty variable: initial value will be undefined", decl.position)
if (decl.datatype != DataType.STR) { else
errors.warn("non-string initonce variable: value will not be reset in subsequent subroutine invocations", decl.position) errors.err("dirty variable can't have initialization value", decl.position)
} }
} }

View File

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

View File

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

View File

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

View File

@ -43,15 +43,16 @@ internal class StatementReorderer(
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if (decl.type == VarDeclType.VAR) { if (decl.type == VarDeclType.VAR) {
if(decl.dirty && decl.value!=null)
errors.err("dirty variable can't have initialization value", decl.position)
if (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL) { if (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL) {
if(decl !in declsProcessedWithInitAssignment) { if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl) declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) { if (decl.value == null) {
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) { if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
if(decl.initOnce) { if(decl.dirty) {
val zerovalue = decl.zeroElementValue() // no initialization at all!
decl.value = zerovalue
zerovalue.linkParents(decl)
return noModifications return noModifications
} }
// A numeric vardecl without an initial value is initialized with zero, // A numeric vardecl without an initial value is initialized with zero,
@ -72,9 +73,6 @@ internal class StatementReorderer(
} }
} }
} else { } else {
if(decl.initOnce) {
return noModifications
}
// Transform the vardecl with initvalue to a plain vardecl + assignment // Transform the vardecl with initvalue to a plain vardecl + assignment
// this allows for other optimizations to kick in. // this allows for other optimizations to kick in.
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99' // So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
@ -231,7 +229,7 @@ internal class StatementReorderer(
it.sharedWithAsm, it.sharedWithAsm,
it.splitArray, it.splitArray,
it.alignment, it.alignment,
it.initOnce, it.dirty,
it.position it.position
) )
IAstModification.ReplaceNode(it, newvar, subroutine) IAstModification.ReplaceNode(it, newvar, subroutine)

View File

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

View File

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

View File

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

View File

@ -236,6 +236,15 @@ when assembling the rest of the code). Example::
byte @shared assemblyVariable = 42 byte @shared assemblyVariable = 42
**uninitialized variables:**
All variables will be initialized by prog8 at startup, they'll get their assigned initialization value, or be cleared to zero.
This (re)initialization is also done on each subroutine entry for the variables declared in the subroutine.
There may be certain scenarios where this initialization is redundant and/or where you want to avoid the overhead of it
You can do so by using the ``@dirty`` tag on the variable declaration.
This means that the variable will *not* be (re)initialized by Prog8 and that its value is undefined.
You have to assign it a value yourself first, before using the variable. If you don't do that, the value can be anything, so beware.
**memory alignment:** **memory alignment:**
A string or array variable can be aligned to a couple of possible interval sizes in memory. A string or array variable can be aligned to a couple of possible interval sizes in memory.
The use for this is very situational, but two examples are: sprite data for the C64 that needs The use for this is very situational, but two examples are: sprite data for the C64 that needs

View File

@ -385,6 +385,7 @@ Tag Effect
@alignword aligns string or array variable on an even memory address @alignword aligns string or array variable on an even memory address
@align64 aligns string or array variable on a 64 byte address interval (example: for C64 sprite data) @align64 aligns string or array variable on a 64 byte address interval (example: for C64 sprite data)
@alignpage aligns string or array variable on a 256 byte address interval (example: to avoid page boundaries) @alignpage aligns string or array variable on a 256 byte address interval (example: to avoid page boundaries)
@dirty the variable won't be initialized by Prog8 which means that its value is undefined. You'll have to set it yourself before using the variable. Used to reduce overhead in certain scenarios. 🦶🔫 Footgun warning.
========== ====== ========== ======

View File

@ -1,9 +1,7 @@
TODO TODO
==== ====
- BUG: fix @initonce for variables that end up in zeropage. They remain uninitialized altogether now. Fix it or don't allow it? - add unit tests for @dirty variables
- add unit tests for @initonce variables
- add docs about variables with @initonce initialization
for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed. for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed.

View File

@ -1,65 +1,71 @@
%import floats
%import textio %import textio
%option no_sysinit %option no_sysinit
%zeropage basicsafe %zeropage basicsafe
; INIT ONCE tests ; DIRTY tests
main { main {
uword @shared @dirty globw
uword @shared globwi = 4444
float @shared @dirty globf
float @shared globfi = 4
ubyte[5] @shared @dirty globarr1
ubyte[] @shared globarr2 = [11,22,33,44,55]
sub start() { sub start() {
uword w0 testdirty()
uword @initonce w1
uword @initonce w2
uword @initonce w3
uword @initonce w4 = 12345
uword[4] wa
uword[] @shared wb = [1111,2222,3333,4444]
dump()
txt.nl() txt.nl()
w0++ testdirty()
w1++
w2++
w3++
w4++
wa[1]++
wa[2]++
wa[3]++
wb[0]++
dump()
txt.nl()
repeat 10 {
footgun()
}
txt.nl()
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() txt.nl()
} }
}
sub footgun() { sub testdirty() {
; TODO should just be a nonlocal variable outside of the subroutine...? uword @shared @dirty locw
ubyte @shared @initonce @requirezp variable = 42 ; BUG: is never initialized now uword @shared locwi = 4444
txt.print_ub(variable) float @shared @dirty locf
float @shared locfi = 4.0
ubyte[5] @shared @dirty locarr1
ubyte[] @shared locarr2 = [11,22,33,44,55]
txt.print("globals: ")
txt.print_uw(globw)
txt.spc() txt.spc()
variable++ floats.print(globf)
txt.print(" with init: ")
txt.print_uw(globwi)
txt.spc()
floats.print(globfi)
txt.print(" arrays: ")
txt.print_ub(globarr1[2])
txt.spc()
txt.print_ub(globarr2[2])
txt.print("\nlocals: ")
txt.print_uw(locw)
txt.spc()
floats.print(locf)
txt.print(" with init: ")
txt.print_uw(locwi)
txt.spc()
floats.print(locfi)
txt.print(" arrays: ")
txt.print_ub(locarr1[2])
txt.spc()
txt.print_ub(locarr2[2])
txt.nl()
globw++
globwi++
globf++
globfi++
globarr1[2]++
globarr2[2]++
locw++
locwi++
locf++
locfi++
locarr1[2]++
locarr2[2]++
} }
} }

View File

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

View File

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

View File

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