added direct memory access ("poke"/"peek")

This commit is contained in:
Irmen de Jong 2018-12-30 21:40:27 +01:00
parent 75a57da44d
commit 500777bf78
18 changed files with 1119 additions and 963 deletions

View File

@ -120,6 +120,7 @@ assign_target:
| identifier
| scoped_identifier
| arrayindexed
| directmemory
;
postincrdecr : assign_target operator = ('++' | '--') ;
@ -146,6 +147,7 @@ expression :
| identifier
| scoped_identifier
| arrayindexed
| directmemory
| expression typecast
;
@ -157,6 +159,8 @@ arrayindexed :
(identifier | scoped_identifier ) arrayspec
;
directmemory : '@' expression ;
functioncall :
(identifier | scoped_identifier) '(' expression_list? ')'

View File

@ -0,0 +1,21 @@
%import c64lib
%import c64utils
~ main {
sub start() {
c64.STROUT("balloon sprites!\n")
const uword SP0X = $d000 ; @todo "address-of" operator '&' so we can write &c64.SP0X
const uword SP0Y = $d001 ; @todo "address-of" operator '&' so we can write &c64.SP0Y
for ubyte i in 0 to 7 {
@(SP0X+i*2) = 30+i*30
@(SP0Y+i*2) = 100+i*10
}
c64.SPENA = 255 ; enable all sprites
}
}

View File

@ -1,95 +1,17 @@
%import c64utils
%option enable_floats
~ main {
float[10] xcoor = [1,2,3,4,5,6,7,8,9.9,11.11 ]
float[10] ycoor = [11,22,33,44,55,66,77,88,99.9,111.11 ]
float[10] zcoor = [111,222,333,444,555,666,777,888,999.9,1001.11 ]
sub start() {
c64scr.print("\nxcoor:\n")
for float f1 in xcoor {
c64flt.print_f(f1)
c64.CHROUT(',')
}
c64.CHROUT('\n')
c64scr.print("ycoor:\n")
for float f2 in ycoor {
c64flt.print_f(f2)
c64.CHROUT(',')
}
c64.CHROUT('\n')
c64scr.print("zcoor:\n")
for float f3 in zcoor {
c64flt.print_f(f3)
c64.CHROUT(',')
}
c64.CHROUT('\n')
uword vic = $d000
const uword cvic = $d000
c64.CHROUT('X')
c64scr.print_ub(X)
c64.CHROUT('\n')
float avgfx = avg(xcoor)
float avgfy = avg(ycoor)
float avgfz = avg(zcoor)
c64.CHROUT('X')
c64scr.print_ub(X)
c64.CHROUT('\n')
@(cvic+$20) = 7
@(cvic+$21) = @(cvic+$20)
c64scr.print("avgfx=")
c64flt.print_f(avgfx)
c64.CHROUT('\n')
c64scr.print("avgfy=")
c64flt.print_f(avgfy)
c64.CHROUT('\n')
c64scr.print("avgfz=")
c64flt.print_f(avgfz)
c64.CHROUT('\n')
separated2:
c64scr.print("\nseparated i=2\n")
c64scr.print(" x[2]=")
ubyte ii=2
c64flt.print_f(xcoor[ii])
c64scr.print(" y[2]=")
c64flt.print_f(ycoor[ii])
c64scr.print(" z[2]=")
c64flt.print_f(zcoor[ii])
separated3:
c64scr.print("\nseparated i=3\n")
ii=3
c64scr.print(" x[3]=")
c64flt.print_f(xcoor[ii])
c64scr.print(" y[3]=")
c64flt.print_f(ycoor[ii])
c64scr.print(" z[3]=")
c64flt.print_f(zcoor[ii])
c64.CHROUT('\n')
c64.CHROUT('X')
c64scr.print_ub(X)
c64.CHROUT('\n')
avgfx = avg(xcoor)
avgfy = avg(ycoor)
avgfz = avg(zcoor)
c64.CHROUT('X')
c64scr.print_ub(X)
c64.CHROUT('\n')
c64scr.print("avgfx=")
c64flt.print_f(avgfx)
c64.CHROUT('\n')
c64scr.print("avgfy=")
c64flt.print_f(avgfy)
c64.CHROUT('\n')
c64scr.print("avgfz=")
c64flt.print_f(avgfz)
c64.CHROUT('\n')
@(vic+$20) = 5
@(vic+$21) = @(vic+$20)
}
}

View File

@ -261,6 +261,7 @@ interface IAstProcessor {
fun process(assignTarget: AssignTarget): AssignTarget {
assignTarget.arrayindexed?.process(this)
assignTarget.identifier?.process(this)
assignTarget.memAddressExpression = assignTarget.memAddressExpression?.process(this)
return assignTarget
}
@ -273,6 +274,11 @@ interface IAstProcessor {
typecastExpression.expression = typecastExpression.expression.process(this)
return typecastExpression
}
fun process(directmemoryExpression: DirectMemoryExpression): IExpression {
directmemoryExpression.addressExpression = directmemoryExpression.addressExpression.process(this)
return directmemoryExpression
}
}
@ -725,6 +731,7 @@ class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, va
data class AssignTarget(val register: Register?,
val identifier: IdentifierReference?,
val arrayindexed: ArrayIndexedExpression?,
var memAddressExpression: IExpression?,
override val position: Position) : Node {
override lateinit var parent: Node
@ -732,6 +739,7 @@ data class AssignTarget(val register: Register?,
this.parent = parent
identifier?.linkParents(this)
arrayindexed?.linkParents(this)
memAddressExpression?.linkParents(this)
}
fun process(processor: IAstProcessor) = processor.process(this)
@ -739,9 +747,10 @@ data class AssignTarget(val register: Register?,
companion object {
fun fromExpr(expr: IExpression): AssignTarget {
return when (expr) {
is RegisterExpr -> AssignTarget(expr.register, null, null, expr.position)
is IdentifierReference -> AssignTarget(null, expr, null, expr.position)
is ArrayIndexedExpression -> AssignTarget(null, null, expr, expr.position)
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position)
is DirectMemoryExpression -> AssignTarget(null, null, null, expr, expr.position)
else -> throw FatalAstException("invalid expression object $expr")
}
}
@ -761,6 +770,10 @@ data class AssignTarget(val register: Register?,
if(dt!=null)
return dt
}
if(memAddressExpression!=null)
return DataType.UBYTE
return null
}
@ -771,6 +784,8 @@ data class AssignTarget(val register: Register?,
return identifier.nameInSource.last()
if(arrayindexed!=null)
return arrayindexed.identifier!!.nameInSource.last()
if(memAddressExpression is LiteralValue)
return (memAddressExpression as LiteralValue).asIntegerValue.toString()
return "???"
}
}
@ -1012,6 +1027,24 @@ class TypecastExpression(var expression: IExpression, var type: DataType, overri
}
class DirectMemoryExpression(var addressExpression: IExpression, override val position: Position) : IExpression {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
this.addressExpression.linkParents(this)
}
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String) = false
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = DataType.UBYTE
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
override fun constValue(namespace: INameScope, heap: HeapValues) = null
override fun toString(): String {
return "DirectMemory($addressExpression)"
}
}
private data class NumericLiteral(val number: Number, val datatype: DataType)
@ -1942,14 +1975,14 @@ private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
val register = register()?.toAst()
val identifier = identifier()
return when {
register!=null -> AssignTarget(register, null, null, toPosition())
identifier!=null -> AssignTarget(null, identifier.toAst(), null, toPosition())
arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), toPosition())
else -> AssignTarget(null, scoped_identifier()?.toAst(), null, toPosition())
register!=null -> AssignTarget(register, null, null, null, toPosition())
identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition())
arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition())
directmemory()!=null -> AssignTarget(null, null, null, directmemory().expression().toAst(), toPosition())
else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
}
}
private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase())
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
@ -2074,6 +2107,9 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
if(typecast()!=null)
return TypecastExpression(expression(0).toAst(), typecast().datatype().toAst(), toPosition())
if(directmemory()!=null)
return DirectMemoryExpression(directmemory().expression().toAst(), toPosition())
throw FatalAstException(text)
}

View File

@ -387,6 +387,13 @@ class AstChecker(private val namespace: INameScope,
}
private fun processAssignmentTarget(assignment: Assignment, target: AssignTarget): Assignment {
val memAddr = target.memAddressExpression?.constValue(namespace, heap)?.asIntegerValue
if(memAddr!=null) {
if(memAddr<0 || memAddr>=65536)
checkResult.add(ExpressionError("address out of range", target.position))
return assignment
}
if(target.identifier!=null) {
val targetName = target.identifier.nameInSource
val targetSymbol = namespace.lookup(targetName, assignment)

View File

@ -121,7 +121,7 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He
else
declvalue
return VariableInitializationAssignment(
AssignTarget(null, IdentifierReference(decl.scopedname.split("."), decl.position), null, decl.position),
AssignTarget(null, IdentifierReference(decl.scopedname.split("."), decl.position), null, null, decl.position),
null,
value,
decl.position

View File

@ -580,6 +580,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
is ArrayIndexedExpression -> translate(expr, false)
is RangeExpr -> throw CompilerException("it's not possible to just have a range expression that has to be translated")
is TypecastExpression -> translate(expr)
is DirectMemoryExpression -> translate(expr)
else -> {
val lv = expr.constValue(namespace, heap) ?: throw CompilerException("constant expression required, not $expr")
when(lv.type) {
@ -876,7 +877,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
} else {
when (arg.second.registerOrPair!!) {
A -> {
val assign = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, arg.first, callPosition)
val assign = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, arg.first, callPosition)
assign.linkParents(arguments[0].parent)
translate(assign)
}
@ -885,12 +886,12 @@ private class StatementTranslator(private val prog: IntermediateProgram,
prog.instr(Opcode.RSAVEX)
restoreX = true
}
val assign = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, arg.first, callPosition)
val assign = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, arg.first, callPosition)
assign.linkParents(arguments[0].parent)
translate(assign)
}
Y -> {
val assign = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, arg.first, callPosition)
val assign = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, arg.first, callPosition)
assign.linkParents(arguments[0].parent)
translate(assign)
}
@ -906,8 +907,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.UBYTE -> {
valueA=arg.first
valueX=LiteralValue.optimalInteger(0, callPosition)
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, valueA, callPosition)
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, valueX, callPosition)
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, valueA, callPosition)
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, valueX, callPosition)
assignA.linkParents(arguments[0].parent)
assignX.linkParents(arguments[0].parent)
translate(assignA)
@ -932,8 +933,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.UBYTE -> {
valueA=arg.first
valueY=LiteralValue.optimalInteger(0, callPosition)
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, valueA, callPosition)
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, valueY, callPosition)
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, valueA, callPosition)
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, valueY, callPosition)
assignA.linkParents(arguments[0].parent)
assignY.linkParents(arguments[0].parent)
translate(assignA)
@ -962,8 +963,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.UBYTE -> {
valueX=arg.first
valueY=LiteralValue.optimalInteger(0, callPosition)
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, valueX, callPosition)
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, valueY, callPosition)
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, valueX, callPosition)
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, valueY, callPosition)
assignX.linkParents(arguments[0].parent)
assignY.linkParents(arguments[0].parent)
translate(assignX)
@ -1481,6 +1482,17 @@ private class StatementTranslator(private val prog: IntermediateProgram,
}
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.toString())
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it
assignTarget.memAddressExpression != null -> {
val address = assignTarget.memAddressExpression?.constValue(namespace, heap)?.asIntegerValue
if(address!=null) {
// const integer address given
prog.instr(Opcode.POP_MEM_BYTE, arg=Value(DataType.UWORD, address))
} else {
translate(assignTarget.memAddressExpression!!)
prog.instr(Opcode.POP_MEMWRITE)
}
}
else -> throw CompilerException("corrupt assigntarget $assignTarget")
}
}
@ -1710,9 +1722,9 @@ private class StatementTranslator(private val prog: IntermediateProgram,
// loop starts here
prog.label(loopLabel)
val assignTarget = if(loop.loopRegister!=null)
AssignTarget(loop.loopRegister, null, null, loop.position)
AssignTarget(loop.loopRegister, null, null, null, loop.position)
else
AssignTarget(null, loop.loopVar!!.copy(), null, loop.position)
AssignTarget(null, loop.loopVar!!.copy(), null, null, loop.position)
val arrayspec = ArraySpec(IdentifierReference(listOf(ForLoop.iteratorLoopcounterVarname), loop.position), loop.position)
val assignLv = Assignment(
listOf(assignTarget), null,
@ -1843,9 +1855,9 @@ private class StatementTranslator(private val prog: IntermediateProgram,
*/
fun makeAssignmentTarget(): AssignTarget {
return if(varname!=null)
AssignTarget(null, IdentifierReference(varname, range.position), null, range.position)
AssignTarget(null, IdentifierReference(varname, range.position), null, null, range.position)
else
AssignTarget(register, null, null, range.position)
AssignTarget(register, null, null, null, range.position)
}
val startAssignment = Assignment(listOf(makeAssignmentTarget()), null, range.from, range.position)
@ -2071,4 +2083,15 @@ private class StatementTranslator(private val prog: IntermediateProgram,
}
}
private fun translate(expr: DirectMemoryExpression) {
// for now, only a single memory location (ubyte) is read at a time.
val address = expr.addressExpression.constValue(namespace, heap)?.asIntegerValue
if(address!=null) {
prog.instr(Opcode.PUSH_MEM_UB, arg = Value(DataType.UWORD, address))
} else {
translate(expr.addressExpression)
prog.instr(Opcode.PUSH_MEMREAD)
}
}
}

View File

@ -11,6 +11,7 @@ enum class Opcode {
PUSH_MEM_W, // push word value from memory to stack
PUSH_MEM_UW, // push unsigned word value from memory to stack
PUSH_MEM_FLOAT, // push float value from memory to stack
PUSH_MEMREAD, // push memory value from address that's on the stack
PUSH_VAR_BYTE, // push byte variable (ubyte, byte)
PUSH_VAR_WORD, // push word variable (uword, word)
PUSH_VAR_FLOAT, // push float variable
@ -27,6 +28,7 @@ enum class Opcode {
POP_MEM_BYTE, // pop (u)byte value into destination memory address
POP_MEM_WORD, // pop (u)word value into destination memory address
POP_MEM_FLOAT, // pop float value into destination memory address
POP_MEMWRITE, // pop address and byte stack and write the byte to the memory address
POP_VAR_BYTE, // pop (u)byte value into variable
POP_VAR_WORD, // pop (u)word value into variable
POP_VAR_FLOAT, // pop float value into variable

View File

@ -493,6 +493,17 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.PUSH_MEM_FLOAT -> {
" lda #<${hexVal(ins)} | ldy #>${hexVal(ins)}| jsr prog8_lib.push_float"
}
Opcode.PUSH_MEMREAD -> {
"""
lda ${(ESTACK_LO+1).toHex()},x
sta ${C64Zeropage.SCRATCH_W1}
lda ${(ESTACK_HI+1).toHex()},x
sta ${C64Zeropage.SCRATCH_W1+1}
ldy #0
lda (${C64Zeropage.SCRATCH_W1}),y
sta ${(ESTACK_LO+1).toHex()},x
"""
}
Opcode.PUSH_REGAY_WORD -> {
" sta ${ESTACK_LO.toHex()},x | tya | sta ${ESTACK_HI.toHex()},x | dex "
@ -579,6 +590,20 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.POP_MEM_FLOAT -> {
" lda ${hexVal(ins)} | ldy ${hexValPlusOne(ins)} | jsr prog8_lib.pop_float"
}
Opcode.POP_MEMWRITE -> {
"""
inx
lda ${ESTACK_LO.toHex()},x
sta ${C64Zeropage.SCRATCH_W1}
lda ${ESTACK_HI.toHex()},x
sta ${C64Zeropage.SCRATCH_W1+1}
inx
lda ${ESTACK_LO.toHex()},x
ldy #0
sta (${C64Zeropage.SCRATCH_W1}),y
"""
}
Opcode.POP_VAR_BYTE -> {
when (ins.callLabel) {
"X" -> throw CompilerException("makes no sense to pop X, it's used as a stack pointer itself")
@ -702,8 +727,8 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.CAST_F_TO_B -> " jsr prog8_lib.stack_float2w"
Opcode.CAST_F_TO_UW -> " jsr prog8_lib.stack_float2uw"
Opcode.CAST_F_TO_W -> " jsr prog8_lib.stack_float2w"
Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta ${ESTACK_HI+1},x" // clear the msb
Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " ${signExtendA("${ESTACK_HI+1},x")}" // sign extend the lsb @todo missing an lda???
Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta ${(ESTACK_HI+1).toHex()},x" // clear the msb
Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " lda ${(ESTACK_LO+1)},x | ${signExtendA("${(ESTACK_HI+1).toHex()},x")}" // sign extend the lsb
Opcode.MSB -> " lda ${(ESTACK_HI+1).toHex()},x | sta ${(ESTACK_LO+1).toHex()},x"
Opcode.ADD_UB, Opcode.ADD_B -> {

View File

@ -93,7 +93,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
if(range.size(heap)==1) {
// for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block
val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position)), null, range.from, forLoop.position)
val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position)), null, range.from, forLoop.position)
forLoop.body.statements.add(0, assignment)
optimizationsDone++
return forLoop.body

View File

@ -1,4 +1,4 @@
// Generated from ../antlr/prog8.g4 by ANTLR 4.7.2
// Generated from /home/irmen/Projects/prog8/compiler/antlr/prog8.g4 by ANTLR 4.7.2
package prog8.parser;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
@ -75,13 +75,13 @@ public class prog8Lexer extends Lexer {
"'&='", "'|='", "'^='", "'%='", "'++'", "'--'", "'('", "')'", "'+'",
"'-'", "'**'", "'*'", "'/'", "'//'", "'%'", "'<'", "'>'", "'<='", "'>='",
"'=='", "'!='", "'&'", "'^'", "'|'", "'to'", "'step'", "'and'", "'or'",
"'xor'", "'not'", "'as'", "'return'", "'break'", "'continue'", "'.'",
"'A'", "'X'", "'Y'", "'AX'", "'AY'", "'XY'", "'Pc'", "'Pz'", "'Pn'",
"'xor'", "'not'", "'as'", "'@'", "'return'", "'break'", "'continue'",
"'.'", "'A'", "'X'", "'Y'", "'AX'", "'AY'", "'XY'", "'Pc'", "'Pz'", "'Pn'",
"'Pv'", "'.w'", "'true'", "'false'", "'%asm'", "'sub'", "'->'", "'{'",
"'}'", "'asmsub'", "'clobbers'", "'@'", "'if'", "'else'", "'if_cs'",
"'if_cc'", "'if_eq'", "'if_z'", "'if_ne'", "'if_nz'", "'if_pl'", "'if_pos'",
"'if_mi'", "'if_neg'", "'if_vs'", "'if_vc'", "'for'", "'in'", "'while'",
"'repeat'", "'until'"
"'}'", "'asmsub'", "'clobbers'", "'if'", "'else'", "'if_cs'", "'if_cc'",
"'if_eq'", "'if_z'", "'if_ne'", "'if_nz'", "'if_pl'", "'if_pos'", "'if_mi'",
"'if_neg'", "'if_vs'", "'if_vc'", "'for'", "'in'", "'while'", "'repeat'",
"'until'"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
@ -238,11 +238,11 @@ public class prog8Lexer extends Lexer {
")\3)\3*\3*\3+\3+\3,\3,\3-\3-\3.\3.\3.\3/\3/\3\60\3\60\3\61\3\61\3\61\3"+
"\62\3\62\3\63\3\63\3\64\3\64\3\65\3\65\3\65\3\66\3\66\3\66\3\67\3\67\3"+
"\67\38\38\38\39\39\3:\3:\3;\3;\3<\3<\3<\3=\3=\3=\3=\3=\3>\3>\3>\3>\3?"+
"\3?\3?\3@\3@\3@\3@\3A\3A\3A\3A\3B\3B\3B\3C\3C\3C\3C\3C\3C\3C\3D\3D\3D"+
"\3D\3D\3D\3E\3E\3E\3E\3E\3E\3E\3E\3E\3F\3F\3G\3G\3H\3H\3I\3I\3J\3J\3J"+
"\3K\3K\3K\3L\3L\3L\3M\3M\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R"+
"\3R\3R\3R\3S\3S\3S\3S\3S\3S\3T\3T\3T\3T\3T\3U\3U\3U\3U\3V\3V\3V\3W\3W"+
"\3X\3X\3Y\3Y\3Y\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3\\\3\\\3"+
"\3?\3?\3@\3@\3@\3@\3A\3A\3A\3A\3B\3B\3B\3C\3C\3D\3D\3D\3D\3D\3D\3D\3E"+
"\3E\3E\3E\3E\3E\3F\3F\3F\3F\3F\3F\3F\3F\3F\3G\3G\3H\3H\3I\3I\3J\3J\3K"+
"\3K\3K\3L\3L\3L\3M\3M\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R"+
"\3S\3S\3S\3S\3S\3T\3T\3T\3T\3T\3T\3U\3U\3U\3U\3U\3V\3V\3V\3V\3W\3W\3W"+
"\3X\3X\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3[\3[\3[\3[\3[\3[\3\\\3\\\3"+
"\\\3]\3]\3]\3]\3]\3^\3^\3^\3^\3^\3^\3_\3_\3_\3_\3_\3_\3`\3`\3`\3`\3`\3"+
"`\3a\3a\3a\3a\3a\3b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3d\3d\3d\3d\3d\3"+
"d\3e\3e\3e\3e\3e\3e\3e\3f\3f\3f\3f\3f\3f\3g\3g\3g\3g\3g\3g\3g\3h\3h\3"+
@ -304,13 +304,13 @@ public class prog8Lexer extends Lexer {
"\2\2k\u01ef\3\2\2\2m\u01f2\3\2\2\2o\u01f5\3\2\2\2q\u01f8\3\2\2\2s\u01fa"+
"\3\2\2\2u\u01fc\3\2\2\2w\u01fe\3\2\2\2y\u0201\3\2\2\2{\u0206\3\2\2\2}"+
"\u020a\3\2\2\2\177\u020d\3\2\2\2\u0081\u0211\3\2\2\2\u0083\u0215\3\2\2"+
"\2\u0085\u0218\3\2\2\2\u0087\u021f\3\2\2\2\u0089\u0225\3\2\2\2\u008b\u022e"+
"\2\u0085\u0218\3\2\2\2\u0087\u021a\3\2\2\2\u0089\u0221\3\2\2\2\u008b\u0227"+
"\3\2\2\2\u008d\u0230\3\2\2\2\u008f\u0232\3\2\2\2\u0091\u0234\3\2\2\2\u0093"+
"\u0236\3\2\2\2\u0095\u0239\3\2\2\2\u0097\u023c\3\2\2\2\u0099\u023f\3\2"+
"\2\2\u009b\u0242\3\2\2\2\u009d\u0245\3\2\2\2\u009f\u0248\3\2\2\2\u00a1"+
"\u024b\3\2\2\2\u00a3\u024e\3\2\2\2\u00a5\u0253\3\2\2\2\u00a7\u0259\3\2"+
"\2\2\u00a9\u025e\3\2\2\2\u00ab\u0262\3\2\2\2\u00ad\u0265\3\2\2\2\u00af"+
"\u0267\3\2\2\2\u00b1\u0269\3\2\2\2\u00b3\u0270\3\2\2\2\u00b5\u0279\3\2"+
"\u0236\3\2\2\2\u0095\u0238\3\2\2\2\u0097\u023b\3\2\2\2\u0099\u023e\3\2"+
"\2\2\u009b\u0241\3\2\2\2\u009d\u0244\3\2\2\2\u009f\u0247\3\2\2\2\u00a1"+
"\u024a\3\2\2\2\u00a3\u024d\3\2\2\2\u00a5\u0250\3\2\2\2\u00a7\u0255\3\2"+
"\2\2\u00a9\u025b\3\2\2\2\u00ab\u0260\3\2\2\2\u00ad\u0264\3\2\2\2\u00af"+
"\u0267\3\2\2\2\u00b1\u0269\3\2\2\2\u00b3\u026b\3\2\2\2\u00b5\u0272\3\2"+
"\2\2\u00b7\u027b\3\2\2\2\u00b9\u027e\3\2\2\2\u00bb\u0283\3\2\2\2\u00bd"+
"\u0289\3\2\2\2\u00bf\u028f\3\2\2\2\u00c1\u0295\3\2\2\2\u00c3\u029a\3\2"+
"\2\2\u00c5\u02a0\3\2\2\2\u00c7\u02a6\3\2\2\2\u00c9\u02ac\3\2\2\2\u00cb"+
@ -395,68 +395,68 @@ public class prog8Lexer extends Lexer {
"\7z\2\2\u020e\u020f\7q\2\2\u020f\u0210\7t\2\2\u0210\u0080\3\2\2\2\u0211"+
"\u0212\7p\2\2\u0212\u0213\7q\2\2\u0213\u0214\7v\2\2\u0214\u0082\3\2\2"+
"\2\u0215\u0216\7c\2\2\u0216\u0217\7u\2\2\u0217\u0084\3\2\2\2\u0218\u0219"+
"\7t\2\2\u0219\u021a\7g\2\2\u021a\u021b\7v\2\2\u021b\u021c\7w\2\2\u021c"+
"\u021d\7t\2\2\u021d\u021e\7p\2\2\u021e\u0086\3\2\2\2\u021f\u0220\7d\2"+
"\2\u0220\u0221\7t\2\2\u0221\u0222\7g\2\2\u0222\u0223\7c\2\2\u0223\u0224"+
"\7m\2\2\u0224\u0088\3\2\2\2\u0225\u0226\7e\2\2\u0226\u0227\7q\2\2\u0227"+
"\u0228\7p\2\2\u0228\u0229\7v\2\2\u0229\u022a\7k\2\2\u022a\u022b\7p\2\2"+
"\u022b\u022c\7w\2\2\u022c\u022d\7g\2\2\u022d\u008a\3\2\2\2\u022e\u022f"+
"\7\60\2\2\u022f\u008c\3\2\2\2\u0230\u0231\7C\2\2\u0231\u008e\3\2\2\2\u0232"+
"\u0233\7Z\2\2\u0233\u0090\3\2\2\2\u0234\u0235\7[\2\2\u0235\u0092\3\2\2"+
"\2\u0236\u0237\7C\2\2\u0237\u0238\7Z\2\2\u0238\u0094\3\2\2\2\u0239\u023a"+
"\7C\2\2\u023a\u023b\7[\2\2\u023b\u0096\3\2\2\2\u023c\u023d\7Z\2\2\u023d"+
"\u023e\7[\2\2\u023e\u0098\3\2\2\2\u023f\u0240\7R\2\2\u0240\u0241\7e\2"+
"\2\u0241\u009a\3\2\2\2\u0242\u0243\7R\2\2\u0243\u0244\7|\2\2\u0244\u009c"+
"\3\2\2\2\u0245\u0246\7R\2\2\u0246\u0247\7p\2\2\u0247\u009e\3\2\2\2\u0248"+
"\u0249\7R\2\2\u0249\u024a\7x\2\2\u024a\u00a0\3\2\2\2\u024b\u024c\7\60"+
"\2\2\u024c\u024d\7y\2\2\u024d\u00a2\3\2\2\2\u024e\u024f\7v\2\2\u024f\u0250"+
"\7t\2\2\u0250\u0251\7w\2\2\u0251\u0252\7g\2\2\u0252\u00a4\3\2\2\2\u0253"+
"\u0254\7h\2\2\u0254\u0255\7c\2\2\u0255\u0256\7n\2\2\u0256\u0257\7u\2\2"+
"\u0257\u0258\7g\2\2\u0258\u00a6\3\2\2\2\u0259\u025a\7\'\2\2\u025a\u025b"+
"\7c\2\2\u025b\u025c\7u\2\2\u025c\u025d\7o\2\2\u025d\u00a8\3\2\2\2\u025e"+
"\u025f\7u\2\2\u025f\u0260\7w\2\2\u0260\u0261\7d\2\2\u0261\u00aa\3\2\2"+
"\2\u0262\u0263\7/\2\2\u0263\u0264\7@\2\2\u0264\u00ac\3\2\2\2\u0265\u0266"+
"\7}\2\2\u0266\u00ae\3\2\2\2\u0267\u0268\7\177\2\2\u0268\u00b0\3\2\2\2"+
"\u0269\u026a\7c\2\2\u026a\u026b\7u\2\2\u026b\u026c\7o\2\2\u026c\u026d"+
"\7u\2\2\u026d\u026e\7w\2\2\u026e\u026f\7d\2\2\u026f\u00b2\3\2\2\2\u0270"+
"\u0271\7e\2\2\u0271\u0272\7n\2\2\u0272\u0273\7q\2\2\u0273\u0274\7d\2\2"+
"\u0274\u0275\7d\2\2\u0275\u0276\7g\2\2\u0276\u0277\7t\2\2\u0277\u0278"+
"\7u\2\2\u0278\u00b4\3\2\2\2\u0279\u027a\7B\2\2\u027a\u00b6\3\2\2\2\u027b"+
"\u027c\7k\2\2\u027c\u027d\7h\2\2\u027d\u00b8\3\2\2\2\u027e\u027f\7g\2"+
"\2\u027f\u0280\7n\2\2\u0280\u0281\7u\2\2\u0281\u0282\7g\2\2\u0282\u00ba"+
"\3\2\2\2\u0283\u0284\7k\2\2\u0284\u0285\7h\2\2\u0285\u0286\7a\2\2\u0286"+
"\u0287\7e\2\2\u0287\u0288\7u\2\2\u0288\u00bc\3\2\2\2\u0289\u028a\7k\2"+
"\2\u028a\u028b\7h\2\2\u028b\u028c\7a\2\2\u028c\u028d\7e\2\2\u028d\u028e"+
"\7e\2\2\u028e\u00be\3\2\2\2\u028f\u0290\7k\2\2\u0290\u0291\7h\2\2\u0291"+
"\u0292\7a\2\2\u0292\u0293\7g\2\2\u0293\u0294\7s\2\2\u0294\u00c0\3\2\2"+
"\2\u0295\u0296\7k\2\2\u0296\u0297\7h\2\2\u0297\u0298\7a\2\2\u0298\u0299"+
"\7|\2\2\u0299\u00c2\3\2\2\2\u029a\u029b\7k\2\2\u029b\u029c\7h\2\2\u029c"+
"\u029d\7a\2\2\u029d\u029e\7p\2\2\u029e\u029f\7g\2\2\u029f\u00c4\3\2\2"+
"\2\u02a0\u02a1\7k\2\2\u02a1\u02a2\7h\2\2\u02a2\u02a3\7a\2\2\u02a3\u02a4"+
"\7p\2\2\u02a4\u02a5\7|\2\2\u02a5\u00c6\3\2\2\2\u02a6\u02a7\7k\2\2\u02a7"+
"\u02a8\7h\2\2\u02a8\u02a9\7a\2\2\u02a9\u02aa\7r\2\2\u02aa\u02ab\7n\2\2"+
"\u02ab\u00c8\3\2\2\2\u02ac\u02ad\7k\2\2\u02ad\u02ae\7h\2\2\u02ae\u02af"+
"\7a\2\2\u02af\u02b0\7r\2\2\u02b0\u02b1\7q\2\2\u02b1\u02b2\7u\2\2\u02b2"+
"\u00ca\3\2\2\2\u02b3\u02b4\7k\2\2\u02b4\u02b5\7h\2\2\u02b5\u02b6\7a\2"+
"\2\u02b6\u02b7\7o\2\2\u02b7\u02b8\7k\2\2\u02b8\u00cc\3\2\2\2\u02b9\u02ba"+
"\7k\2\2\u02ba\u02bb\7h\2\2\u02bb\u02bc\7a\2\2\u02bc\u02bd\7p\2\2\u02bd"+
"\u02be\7g\2\2\u02be\u02bf\7i\2\2\u02bf\u00ce\3\2\2\2\u02c0\u02c1\7k\2"+
"\2\u02c1\u02c2\7h\2\2\u02c2\u02c3\7a\2\2\u02c3\u02c4\7x\2\2\u02c4\u02c5"+
"\7u\2\2\u02c5\u00d0\3\2\2\2\u02c6\u02c7\7k\2\2\u02c7\u02c8\7h\2\2\u02c8"+
"\u02c9\7a\2\2\u02c9\u02ca\7x\2\2\u02ca\u02cb\7e\2\2\u02cb\u00d2\3\2\2"+
"\2\u02cc\u02cd\7h\2\2\u02cd\u02ce\7q\2\2\u02ce\u02cf\7t\2\2\u02cf\u00d4"+
"\3\2\2\2\u02d0\u02d1\7k\2\2\u02d1\u02d2\7p\2\2\u02d2\u00d6\3\2\2\2\u02d3"+
"\u02d4\7y\2\2\u02d4\u02d5\7j\2\2\u02d5\u02d6\7k\2\2\u02d6\u02d7\7n\2\2"+
"\u02d7\u02d8\7g\2\2\u02d8\u00d8\3\2\2\2\u02d9\u02da\7t\2\2\u02da\u02db"+
"\7g\2\2\u02db\u02dc\7r\2\2\u02dc\u02dd\7g\2\2\u02dd\u02de\7c\2\2\u02de"+
"\u02df\7v\2\2\u02df\u00da\3\2\2\2\u02e0\u02e1\7w\2\2\u02e1\u02e2\7p\2"+
"\2\u02e2\u02e3\7v\2\2\u02e3\u02e4\7k\2\2\u02e4\u02e5\7n\2\2\u02e5\u00dc"+
"\3\2\2\2\u02e6\u02ea\t\2\2\2\u02e7\u02e9\t\3\2\2\u02e8\u02e7\3\2\2\2\u02e9"+
"\u02ec\3\2\2\2\u02ea\u02e8\3\2\2\2\u02ea\u02eb\3\2\2\2\u02eb\u02ed\3\2"+
"\2\2\u02ec\u02ea\3\2\2\2\u02ed\u02ee\5\u00dfp\2\u02ee\u02ef\3\2\2\2\u02ef"+
"\u02f0\bo\2\2\u02f0\u00de\3\2\2\2\u02f1\u02f5\7=\2\2\u02f2\u02f4\n\2\2"+
"\2\u02f3\u02f2\3\2\2\2\u02f4\u02f7\3\2\2\2\u02f5\u02f3\3\2\2\2\u02f5\u02f6"+
"\3\2\2\2\u02f6\u02f8\3\2\2\2\u02f7\u02f5\3\2\2\2\u02f8\u02f9\bp\2\2\u02f9"+
"\7B\2\2\u0219\u0086\3\2\2\2\u021a\u021b\7t\2\2\u021b\u021c\7g\2\2\u021c"+
"\u021d\7v\2\2\u021d\u021e\7w\2\2\u021e\u021f\7t\2\2\u021f\u0220\7p\2\2"+
"\u0220\u0088\3\2\2\2\u0221\u0222\7d\2\2\u0222\u0223\7t\2\2\u0223\u0224"+
"\7g\2\2\u0224\u0225\7c\2\2\u0225\u0226\7m\2\2\u0226\u008a\3\2\2\2\u0227"+
"\u0228\7e\2\2\u0228\u0229\7q\2\2\u0229\u022a\7p\2\2\u022a\u022b\7v\2\2"+
"\u022b\u022c\7k\2\2\u022c\u022d\7p\2\2\u022d\u022e\7w\2\2\u022e\u022f"+
"\7g\2\2\u022f\u008c\3\2\2\2\u0230\u0231\7\60\2\2\u0231\u008e\3\2\2\2\u0232"+
"\u0233\7C\2\2\u0233\u0090\3\2\2\2\u0234\u0235\7Z\2\2\u0235\u0092\3\2\2"+
"\2\u0236\u0237\7[\2\2\u0237\u0094\3\2\2\2\u0238\u0239\7C\2\2\u0239\u023a"+
"\7Z\2\2\u023a\u0096\3\2\2\2\u023b\u023c\7C\2\2\u023c\u023d\7[\2\2\u023d"+
"\u0098\3\2\2\2\u023e\u023f\7Z\2\2\u023f\u0240\7[\2\2\u0240\u009a\3\2\2"+
"\2\u0241\u0242\7R\2\2\u0242\u0243\7e\2\2\u0243\u009c\3\2\2\2\u0244\u0245"+
"\7R\2\2\u0245\u0246\7|\2\2\u0246\u009e\3\2\2\2\u0247\u0248\7R\2\2\u0248"+
"\u0249\7p\2\2\u0249\u00a0\3\2\2\2\u024a\u024b\7R\2\2\u024b\u024c\7x\2"+
"\2\u024c\u00a2\3\2\2\2\u024d\u024e\7\60\2\2\u024e\u024f\7y\2\2\u024f\u00a4"+
"\3\2\2\2\u0250\u0251\7v\2\2\u0251\u0252\7t\2\2\u0252\u0253\7w\2\2\u0253"+
"\u0254\7g\2\2\u0254\u00a6\3\2\2\2\u0255\u0256\7h\2\2\u0256\u0257\7c\2"+
"\2\u0257\u0258\7n\2\2\u0258\u0259\7u\2\2\u0259\u025a\7g\2\2\u025a\u00a8"+
"\3\2\2\2\u025b\u025c\7\'\2\2\u025c\u025d\7c\2\2\u025d\u025e\7u\2\2\u025e"+
"\u025f\7o\2\2\u025f\u00aa\3\2\2\2\u0260\u0261\7u\2\2\u0261\u0262\7w\2"+
"\2\u0262\u0263\7d\2\2\u0263\u00ac\3\2\2\2\u0264\u0265\7/\2\2\u0265\u0266"+
"\7@\2\2\u0266\u00ae\3\2\2\2\u0267\u0268\7}\2\2\u0268\u00b0\3\2\2\2\u0269"+
"\u026a\7\177\2\2\u026a\u00b2\3\2\2\2\u026b\u026c\7c\2\2\u026c\u026d\7"+
"u\2\2\u026d\u026e\7o\2\2\u026e\u026f\7u\2\2\u026f\u0270\7w\2\2\u0270\u0271"+
"\7d\2\2\u0271\u00b4\3\2\2\2\u0272\u0273\7e\2\2\u0273\u0274\7n\2\2\u0274"+
"\u0275\7q\2\2\u0275\u0276\7d\2\2\u0276\u0277\7d\2\2\u0277\u0278\7g\2\2"+
"\u0278\u0279\7t\2\2\u0279\u027a\7u\2\2\u027a\u00b6\3\2\2\2\u027b\u027c"+
"\7k\2\2\u027c\u027d\7h\2\2\u027d\u00b8\3\2\2\2\u027e\u027f\7g\2\2\u027f"+
"\u0280\7n\2\2\u0280\u0281\7u\2\2\u0281\u0282\7g\2\2\u0282\u00ba\3\2\2"+
"\2\u0283\u0284\7k\2\2\u0284\u0285\7h\2\2\u0285\u0286\7a\2\2\u0286\u0287"+
"\7e\2\2\u0287\u0288\7u\2\2\u0288\u00bc\3\2\2\2\u0289\u028a\7k\2\2\u028a"+
"\u028b\7h\2\2\u028b\u028c\7a\2\2\u028c\u028d\7e\2\2\u028d\u028e\7e\2\2"+
"\u028e\u00be\3\2\2\2\u028f\u0290\7k\2\2\u0290\u0291\7h\2\2\u0291\u0292"+
"\7a\2\2\u0292\u0293\7g\2\2\u0293\u0294\7s\2\2\u0294\u00c0\3\2\2\2\u0295"+
"\u0296\7k\2\2\u0296\u0297\7h\2\2\u0297\u0298\7a\2\2\u0298\u0299\7|\2\2"+
"\u0299\u00c2\3\2\2\2\u029a\u029b\7k\2\2\u029b\u029c\7h\2\2\u029c\u029d"+
"\7a\2\2\u029d\u029e\7p\2\2\u029e\u029f\7g\2\2\u029f\u00c4\3\2\2\2\u02a0"+
"\u02a1\7k\2\2\u02a1\u02a2\7h\2\2\u02a2\u02a3\7a\2\2\u02a3\u02a4\7p\2\2"+
"\u02a4\u02a5\7|\2\2\u02a5\u00c6\3\2\2\2\u02a6\u02a7\7k\2\2\u02a7\u02a8"+
"\7h\2\2\u02a8\u02a9\7a\2\2\u02a9\u02aa\7r\2\2\u02aa\u02ab\7n\2\2\u02ab"+
"\u00c8\3\2\2\2\u02ac\u02ad\7k\2\2\u02ad\u02ae\7h\2\2\u02ae\u02af\7a\2"+
"\2\u02af\u02b0\7r\2\2\u02b0\u02b1\7q\2\2\u02b1\u02b2\7u\2\2\u02b2\u00ca"+
"\3\2\2\2\u02b3\u02b4\7k\2\2\u02b4\u02b5\7h\2\2\u02b5\u02b6\7a\2\2\u02b6"+
"\u02b7\7o\2\2\u02b7\u02b8\7k\2\2\u02b8\u00cc\3\2\2\2\u02b9\u02ba\7k\2"+
"\2\u02ba\u02bb\7h\2\2\u02bb\u02bc\7a\2\2\u02bc\u02bd\7p\2\2\u02bd\u02be"+
"\7g\2\2\u02be\u02bf\7i\2\2\u02bf\u00ce\3\2\2\2\u02c0\u02c1\7k\2\2\u02c1"+
"\u02c2\7h\2\2\u02c2\u02c3\7a\2\2\u02c3\u02c4\7x\2\2\u02c4\u02c5\7u\2\2"+
"\u02c5\u00d0\3\2\2\2\u02c6\u02c7\7k\2\2\u02c7\u02c8\7h\2\2\u02c8\u02c9"+
"\7a\2\2\u02c9\u02ca\7x\2\2\u02ca\u02cb\7e\2\2\u02cb\u00d2\3\2\2\2\u02cc"+
"\u02cd\7h\2\2\u02cd\u02ce\7q\2\2\u02ce\u02cf\7t\2\2\u02cf\u00d4\3\2\2"+
"\2\u02d0\u02d1\7k\2\2\u02d1\u02d2\7p\2\2\u02d2\u00d6\3\2\2\2\u02d3\u02d4"+
"\7y\2\2\u02d4\u02d5\7j\2\2\u02d5\u02d6\7k\2\2\u02d6\u02d7\7n\2\2\u02d7"+
"\u02d8\7g\2\2\u02d8\u00d8\3\2\2\2\u02d9\u02da\7t\2\2\u02da\u02db\7g\2"+
"\2\u02db\u02dc\7r\2\2\u02dc\u02dd\7g\2\2\u02dd\u02de\7c\2\2\u02de\u02df"+
"\7v\2\2\u02df\u00da\3\2\2\2\u02e0\u02e1\7w\2\2\u02e1\u02e2\7p\2\2\u02e2"+
"\u02e3\7v\2\2\u02e3\u02e4\7k\2\2\u02e4\u02e5\7n\2\2\u02e5\u00dc\3\2\2"+
"\2\u02e6\u02ea\t\2\2\2\u02e7\u02e9\t\3\2\2\u02e8\u02e7\3\2\2\2\u02e9\u02ec"+
"\3\2\2\2\u02ea\u02e8\3\2\2\2\u02ea\u02eb\3\2\2\2\u02eb\u02ed\3\2\2\2\u02ec"+
"\u02ea\3\2\2\2\u02ed\u02ee\5\u00dfp\2\u02ee\u02ef\3\2\2\2\u02ef\u02f0"+
"\bo\2\2\u02f0\u00de\3\2\2\2\u02f1\u02f5\7=\2\2\u02f2\u02f4\n\2\2\2\u02f3"+
"\u02f2\3\2\2\2\u02f4\u02f7\3\2\2\2\u02f5\u02f3\3\2\2\2\u02f5\u02f6\3\2"+
"\2\2\u02f6\u02f8\3\2\2\2\u02f7\u02f5\3\2\2\2\u02f8\u02f9\bp\2\2\u02f9"+
"\u00e0\3\2\2\2\u02fa\u02fb\t\3\2\2\u02fb\u02fc\3\2\2\2\u02fc\u02fd\bq"+
"\3\2\u02fd\u00e2\3\2\2\2\u02fe\u0300\t\2\2\2\u02ff\u02fe\3\2\2\2\u0300"+
"\u0301\3\2\2\2\u0301\u02ff\3\2\2\2\u0301\u0302\3\2\2\2\u0302\u00e4\3\2"+

File diff suppressed because it is too large Load Diff

View File

@ -277,6 +277,11 @@ class StackVm(private var traceOutputFile: String?) {
val address = ins.arg!!.integerValue()
evalstack.push(Value(DataType.FLOAT, mem.getFloat(address)))
}
Opcode.PUSH_MEMREAD -> {
val address = evalstack.pop()
checkDt(address, DataType.UWORD)
TODO("push_memread from $address")
}
Opcode.DISCARD_BYTE -> {
val value = evalstack.pop()
checkDt(value, DataType.UBYTE)
@ -313,6 +318,13 @@ class StackVm(private var traceOutputFile: String?) {
val address = ins.arg!!.integerValue()
mem.setFloat(address, value.numericValue().toDouble())
}
Opcode.POP_MEMWRITE -> {
val address = evalstack.pop()
checkDt(address, DataType.UWORD)
val value = evalstack.pop()
checkDt(value, DataType.UBYTE)
TODO("pop_memwrite $value to $address")
}
Opcode.ADD_UB -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UBYTE)

View File

@ -5,7 +5,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="jdk" jdkName="Python 3.7" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -291,6 +291,24 @@ are banked in (and your code imports the ``c64lib.p8``)
The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (negative: **-1.7014118345e+38**)
Converting types into other types
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sometimes you need an unsigned word where you have an unsigned byte, or you need some other type conversion.
Many type conversions are possible by just writing ``as <type>`` at the end of an expression::
uword uw = $ea31
ubyte ub = uw as ubyte ; ub will be $31, identical to lsb(uw)
float f = uw as float ; f will be 59953, but this conversion can be omitted in this case
word w = uw as word ; w will be -5583 (simply reinterpret $ea31 as 2-complement negative number)
f = 56.777
ub = f as ubyte ; ub will be 56
Sometimes it is a straight 'type cast' where the value is simply interpreted as being of the other type,
sometimes an actual value conversion is done to convert it into the targe type.
Try to avoid type conversions as much as possible.
Initial values across multiple runs of the program
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -305,21 +323,6 @@ expected when the program is restarted.
(This is an optimization choice to avoid having to store two copies of every string and array)
Indirect addressing and address-of
----------------------------------
The ``#`` operator is used to take the address of the symbol following it.
It can be used for example to work with the *address* of a memory mapped variable rather than
the value it holds. You could take the address of a string as well, but that is redundant:
the compiler already treats those as a value that you manipulate via its address.
For most other types this prefix is not supported and will result in a compilation error.
The resulting value is simply a 16 bit word.
.. todo::
This is not yet implemented.
Indirect addressing, Indirect addressing in jumps (jmp/jsr indirect)
Loops
-----
@ -411,6 +414,19 @@ a fixed amount of memory which will not change.
that there is a loss of precision. You can use builtin functions such as ``round`` and ``lsb`` to convert
to a smaller datatype, or revert to integer arithmetic.
Direct access to memory locations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Normally memory locations are accessed by a *memory mapped* name, such as ``c64.BGCOL0`` that is defined
as the memory mapped address $d021.
If you want to access a memory location directly (by using the address itself), without defining
a memory mapped location, you can do so by prefixing the address with ``@``::
A = @$d020 ; set the A register to the current c64 screen border color ("peek(53280)")
@$d020 = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address
Expressions
-----------

View File

@ -280,6 +280,12 @@ of something with an operand starting with 1 or 0, you'll have to add a space in
**@todo pointers/addresses? (as opposed to normal WORDs)**
Data type conversion
^^^^^^^^^^^^^^^^^^^^
Many type conversions are possible by just writing ``as <type>`` at the end of an expression,
for example ``ubyte ub = floatvalue as ubyte`` will convert the floating point value to an unsigned byte.
Memory mapped variables
^^^^^^^^^^^^^^^^^^^^^^^
@ -290,6 +296,16 @@ should be the *memory address* where the value is located::
memory byte BORDER = $d020
Direct access to memory locations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Instead of defining a memory mapped name for a specific memory location, you can also
directly access the memory. Prefix a numeric expression or literal by ``@`` to do that::
A = @$d020 ; set the A register to the current c64 screen border color ("peek(53280)")
@$d020 = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
Constants
^^^^^^^^^

View File

@ -29,6 +29,15 @@
const uword Screen = $0400 ; default character screen matrix @todo matrix/array? needs to support array size > 255
const uword Colors = $d800 ; character screen colors @todo matrix/array? needs to support array size > 255
memory ubyte SPRPTR0 = 2040 ; default sprite pointers (store address of sprite / 64)
memory ubyte SPRPTR1 = 2041
memory ubyte SPRPTR2 = 2042
memory ubyte SPRPTR3 = 2043
memory ubyte SPRPTR4 = 2044
memory ubyte SPRPTR5 = 2045
memory ubyte SPRPTR6 = 2046
memory ubyte SPRPTR7 = 2047
; ---- VIC-II registers ----

View File

@ -476,6 +476,7 @@ abs_f .proc
add_w .proc
; -- push word+word / uword+uword
; @todo INLINE THIS
inx
clc
lda ESTACK_LO,x
@ -489,6 +490,7 @@ add_w .proc
sub_w .proc
; -- push word-word
; @todo INLINE THIS
inx
sec
lda ESTACK_LO+1,x