diff --git a/compiler/src/prog8/astvm/AstVm.kt b/compiler/src/prog8/astvm/AstVm.kt index ed76bc7b8..a724a3e58 100644 --- a/compiler/src/prog8/astvm/AstVm.kt +++ b/compiler/src/prog8/astvm/AstVm.kt @@ -3,6 +3,7 @@ package prog8.astvm import prog8.ast.* import prog8.compiler.RuntimeValue import prog8.compiler.RuntimeValueRange +import prog8.compiler.target.c64.Petscii import java.awt.EventQueue @@ -217,9 +218,24 @@ class AstVm(val program: Program) { if(value.type!=DataType.FLOAT) throw VmExecutionException("new value is of different datatype ${value.type} for $array") } + DataType.STR, DataType.STR_S -> { + if(value.type !in ByteDatatypes) + throw VmExecutionException("new value is of different datatype ${value.type} for $array") + } else -> throw VmExecutionException("strange array type ${array.type}") } - array.array!![index.integerValue()] = value.numericValue() + if(array.type in ArrayDatatypes) + array.array!![index.integerValue()] = value.numericValue() + else if(array.type in StringDatatypes) { + val index = index.integerValue() + val newchr = Petscii.decodePetscii(listOf(value.numericValue().toShort()), true) + val newstr = array.str!!.replaceRange(index, index+1, newchr) + val ident = stmt.definingScope().lookup(target.arrayindexed.identifier.nameInSource, stmt) as? VarDecl + ?: throw VmExecutionException("can't find assignment target ${target.identifier}") + val identScope = ident.definingScope() + program.heap.update(array.heapId!!, newstr) + runtimeVariables.set(identScope, ident.name, RuntimeValue(array.type, str=newstr, heapId=array.heapId)) + } } target.register!=null -> { runtimeVariables.set(program.namespace, target.register.name, value) diff --git a/compiler/src/prog8/astvm/BuiltinFunctions.kt b/compiler/src/prog8/astvm/BuiltinFunctions.kt index d107c6265..62c5e404b 100644 --- a/compiler/src/prog8/astvm/BuiltinFunctions.kt +++ b/compiler/src/prog8/astvm/BuiltinFunctions.kt @@ -14,6 +14,15 @@ class BuiltinFunctions { "rnd" -> RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255) "rndw" -> RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535) "rndf" -> RuntimeValue(DataType.FLOAT, rnd.nextDouble()) + "lsb" -> RuntimeValue(DataType.UBYTE, args[0].integerValue() and 255) + "msb" -> RuntimeValue(DataType.UBYTE, (args[0].integerValue() ushr 8) and 255) + "strlen" -> { + val zeroIndex = args[0].str!!.indexOf(0.toChar()) + if(zeroIndex>=0) + RuntimeValue(DataType.UBYTE, zeroIndex) + else + RuntimeValue(DataType.UBYTE, args[0].str!!.length) + } "memset" -> { val target = args[0].array!! val amount = args[1].integerValue() diff --git a/compiler/src/prog8/astvm/ScreenDialog.kt b/compiler/src/prog8/astvm/ScreenDialog.kt index e84140f7a..8f6ffcedb 100644 --- a/compiler/src/prog8/astvm/ScreenDialog.kt +++ b/compiler/src/prog8/astvm/ScreenDialog.kt @@ -61,7 +61,8 @@ class BitmapScreenPanel : KeyListener, JPanel() { g2d.drawLine(x1, y1, x2, y2) } fun printText(text: String, color: Int, lowercase: Boolean) { - val lines = text.split('\n') + val t2 = text.substringBefore(0.toChar()) + val lines = t2.split('\n') for(line in lines.withIndex()) { printTextSingleLine(line.value, color, lowercase) if(line.index>", 0, 0, 0) val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, false, Register.A.name, LiteralValue.optimalInteger(0, globalpos), globalpos) - val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, false, Register.X.name, LiteralValue.optimalInteger(0, globalpos), globalpos) + val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, false, Register.X.name, LiteralValue.optimalInteger(255, globalpos), globalpos) val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, false, Register.Y.name, LiteralValue.optimalInteger(0, globalpos), globalpos) vdA.linkParents(program.namespace) vdX.linkParents(program.namespace) @@ -27,25 +27,33 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v } override fun process(decl: VarDecl): IStatement { - if(decl.type==VarDeclType.VAR) { - val value = when (decl.datatype) { - in NumericDatatypes -> { - if(decl.value !is LiteralValue) { - TODO("evaluate vardecl expression $decl") - //RuntimeValue(decl.datatype, num = evaluate(decl.value!!, program, runtimeVariables, executeSubroutine).numericValue()) - } else { + when(decl.type) { + VarDeclType.VAR -> { + val value = when (decl.datatype) { + in NumericDatatypes -> { + if(decl.value !is LiteralValue) { + TODO("evaluate vardecl expression $decl") + //RuntimeValue(decl.datatype, num = evaluate(decl.value!!, program, runtimeVariables, executeSubroutine).numericValue()) + } else { + RuntimeValue.from(decl.value as LiteralValue, heap) + } + } + in StringDatatypes -> { RuntimeValue.from(decl.value as LiteralValue, heap) } + in ArrayDatatypes -> { + RuntimeValue.from(decl.value as LiteralValue, heap) + } + else -> throw VmExecutionException("weird type ${decl.datatype}") } - in StringDatatypes -> { - RuntimeValue.from(decl.value as LiteralValue, heap) - } - in ArrayDatatypes -> { - RuntimeValue.from(decl.value as LiteralValue, heap) - } - else -> throw VmExecutionException("weird type ${decl.datatype}") + runtimeVariables.define(decl.definingScope(), decl.name, value) + } + VarDeclType.MEMORY -> { + // TODO register memory mapped vars? + } + VarDeclType.CONST -> { + // consts should have been const-folded away } - runtimeVariables.define(decl.definingScope(), decl.name, value) } return super.process(decl) } diff --git a/compiler/src/prog8/compiler/target/c64/Petscii.kt b/compiler/src/prog8/compiler/target/c64/Petscii.kt index 17fec04ff..fcb0ff00f 100644 --- a/compiler/src/prog8/compiler/target/c64/Petscii.kt +++ b/compiler/src/prog8/compiler/target/c64/Petscii.kt @@ -9,7 +9,7 @@ class Petscii { // character tables used from https://github.com/dj51d/cbmcodecs private val decodingPetsciiLowercase = arrayOf( - '\ufffe', // 0x00 -> UNDEFINED + '\u0000', // 0x00 -> \u0000 '\ufffe', // 0x01 -> UNDEFINED '\ufffe', // 0x02 -> UNDEFINED '\ufffe', // 0x03 -> UNDEFINED @@ -268,7 +268,7 @@ class Petscii { ) private val decodingPetsciiUppercase = arrayOf( - '\ufffe', // 0x00 -> UNDEFINED + '\u0000', // 0x00 -> \u0000 '\ufffe', // 0x01 -> UNDEFINED '\ufffe', // 0x02 -> UNDEFINED '\ufffe', // 0x03 -> UNDEFINED diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index 5f46e7248..9a45d3daa 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -268,6 +268,14 @@ class TestZeropage { @TestInstance(TestInstance.Lifecycle.PER_CLASS) class TestPetscii { + @Test + fun testZero() { + assertThat(Petscii.encodePetscii("\u0000", true), equalTo(listOf(0))) + assertThat(Petscii.encodePetscii("\u0000", false), equalTo(listOf(0))) + assertThat(Petscii.decodePetscii(listOf(0), true), equalTo("\u0000")) + assertThat(Petscii.decodePetscii(listOf(0), false), equalTo("\u0000")) + } + @Test fun testLowercase() { assertThat(Petscii.encodePetscii("hello WORLD 123 @!£", true), equalTo( diff --git a/examples/test.p8 b/examples/test.p8 index d1d00f513..cc12662e2 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,39 +6,20 @@ ~ main { sub start() { - c64scr.print_ub(rnd()) + uword uw = $ab34 + str name = "irmen de jong" + + c64scr.print_ub(len(name)) c64.CHROUT('\n') - c64scr.print_ub(rnd()) + c64scr.print_ub(strlen(name)) c64.CHROUT('\n') - c64scr.print_ub(rnd()) + c64scr.print(name) c64.CHROUT('\n') - c64scr.print_uw(rndw()) - c64.CHROUT('\n') - c64scr.print_uw(rndw()) - c64.CHROUT('\n') - c64flt.print_f(rndf()) - c64.CHROUT('\n') - c64flt.print_f(rndf()) + name[6] = 0 + c64scr.print_ub(strlen(name)) c64.CHROUT('\n') + c64scr.print(name) c64.CHROUT('\n') - A=rnd() - c64scr.print_ub(A) - c64.CHROUT('\n') - A=rnd() - c64scr.print_ub(A) - c64.CHROUT('\n') - A=rnd() - c64scr.print_ub(A) - c64.CHROUT('\n') - A=rnd() - c64scr.print_ub(A) - c64.CHROUT('\n') - A=rnd() - c64scr.print_ub(A) - c64.CHROUT('\n') - A=rnd() - c64scr.print_ub(A) - c64.CHROUT('\n') } }