strlen and strings with zeros in them should terminate at the zero

This commit is contained in:
Irmen de Jong 2019-06-26 02:34:43 +02:00
parent f49eefad6f
commit 060e05c868
7 changed files with 74 additions and 49 deletions

View File

@ -3,6 +3,7 @@ package prog8.astvm
import prog8.ast.* import prog8.ast.*
import prog8.compiler.RuntimeValue import prog8.compiler.RuntimeValue
import prog8.compiler.RuntimeValueRange import prog8.compiler.RuntimeValueRange
import prog8.compiler.target.c64.Petscii
import java.awt.EventQueue import java.awt.EventQueue
@ -217,9 +218,24 @@ class AstVm(val program: Program) {
if(value.type!=DataType.FLOAT) if(value.type!=DataType.FLOAT)
throw VmExecutionException("new value is of different datatype ${value.type} for $array") 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}") 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 -> { target.register!=null -> {
runtimeVariables.set(program.namespace, target.register.name, value) runtimeVariables.set(program.namespace, target.register.name, value)

View File

@ -14,6 +14,15 @@ class BuiltinFunctions {
"rnd" -> RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255) "rnd" -> RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255)
"rndw" -> RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535) "rndw" -> RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535)
"rndf" -> RuntimeValue(DataType.FLOAT, rnd.nextDouble()) "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" -> { "memset" -> {
val target = args[0].array!! val target = args[0].array!!
val amount = args[1].integerValue() val amount = args[1].integerValue()

View File

@ -61,7 +61,8 @@ class BitmapScreenPanel : KeyListener, JPanel() {
g2d.drawLine(x1, y1, x2, y2) g2d.drawLine(x1, y1, x2, y2)
} }
fun printText(text: String, color: Int, lowercase: Boolean) { 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()) { for(line in lines.withIndex()) {
printTextSingleLine(line.value, color, lowercase) printTextSingleLine(line.value, color, lowercase)
if(line.index<lines.size-1) { if(line.index<lines.size-1) {
@ -124,6 +125,8 @@ class BitmapScreenPanel : KeyListener, JPanel() {
g2d.clearRect(8*clearx, 8*y, 8, 8) g2d.clearRect(8*clearx, 8*y, 8, 8)
} }
for(sc in Petscii.encodeScreencode(text, lowercase)) { for(sc in Petscii.encodeScreencode(text, lowercase)) {
if(sc==0.toShort())
break
setChar(xx++, y, sc) setChar(xx++, y, sc)
} }
} }

View File

@ -9,12 +9,12 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
override fun process(program: Program) { override fun process(program: Program) {
// define the three registers as global variables // define the three registers as global variables
runtimeVariables.define(program.namespace, Register.A.name, RuntimeValue(DataType.UBYTE, 0)) runtimeVariables.define(program.namespace, Register.A.name, RuntimeValue(DataType.UBYTE, 0))
runtimeVariables.define(program.namespace, Register.X.name, RuntimeValue(DataType.UBYTE, 0)) runtimeVariables.define(program.namespace, Register.X.name, RuntimeValue(DataType.UBYTE, 255))
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0)) runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0))
val globalpos = Position("<<global>>", 0, 0, 0) val globalpos = Position("<<global>>", 0, 0, 0)
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, false, Register.A.name, LiteralValue.optimalInteger(0, globalpos), globalpos) 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) val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, false, Register.Y.name, LiteralValue.optimalInteger(0, globalpos), globalpos)
vdA.linkParents(program.namespace) vdA.linkParents(program.namespace)
vdX.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 { override fun process(decl: VarDecl): IStatement {
if(decl.type==VarDeclType.VAR) { when(decl.type) {
val value = when (decl.datatype) { VarDeclType.VAR -> {
in NumericDatatypes -> { val value = when (decl.datatype) {
if(decl.value !is LiteralValue) { in NumericDatatypes -> {
TODO("evaluate vardecl expression $decl") if(decl.value !is LiteralValue) {
//RuntimeValue(decl.datatype, num = evaluate(decl.value!!, program, runtimeVariables, executeSubroutine).numericValue()) TODO("evaluate vardecl expression $decl")
} else { //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) 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 -> { runtimeVariables.define(decl.definingScope(), decl.name, value)
RuntimeValue.from(decl.value as LiteralValue, heap) }
} VarDeclType.MEMORY -> {
in ArrayDatatypes -> { // TODO register memory mapped vars?
RuntimeValue.from(decl.value as LiteralValue, heap) }
} VarDeclType.CONST -> {
else -> throw VmExecutionException("weird type ${decl.datatype}") // consts should have been const-folded away
} }
runtimeVariables.define(decl.definingScope(), decl.name, value)
} }
return super.process(decl) return super.process(decl)
} }

View File

@ -9,7 +9,7 @@ class Petscii {
// character tables used from https://github.com/dj51d/cbmcodecs // character tables used from https://github.com/dj51d/cbmcodecs
private val decodingPetsciiLowercase = arrayOf( private val decodingPetsciiLowercase = arrayOf(
'\ufffe', // 0x00 -> UNDEFINED '\u0000', // 0x00 -> \u0000
'\ufffe', // 0x01 -> UNDEFINED '\ufffe', // 0x01 -> UNDEFINED
'\ufffe', // 0x02 -> UNDEFINED '\ufffe', // 0x02 -> UNDEFINED
'\ufffe', // 0x03 -> UNDEFINED '\ufffe', // 0x03 -> UNDEFINED
@ -268,7 +268,7 @@ class Petscii {
) )
private val decodingPetsciiUppercase = arrayOf( private val decodingPetsciiUppercase = arrayOf(
'\ufffe', // 0x00 -> UNDEFINED '\u0000', // 0x00 -> \u0000
'\ufffe', // 0x01 -> UNDEFINED '\ufffe', // 0x01 -> UNDEFINED
'\ufffe', // 0x02 -> UNDEFINED '\ufffe', // 0x02 -> UNDEFINED
'\ufffe', // 0x03 -> UNDEFINED '\ufffe', // 0x03 -> UNDEFINED

View File

@ -268,6 +268,14 @@ class TestZeropage {
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestPetscii { class TestPetscii {
@Test
fun testZero() {
assertThat(Petscii.encodePetscii("\u0000", true), equalTo(listOf<Short>(0)))
assertThat(Petscii.encodePetscii("\u0000", false), equalTo(listOf<Short>(0)))
assertThat(Petscii.decodePetscii(listOf(0), true), equalTo("\u0000"))
assertThat(Petscii.decodePetscii(listOf(0), false), equalTo("\u0000"))
}
@Test @Test
fun testLowercase() { fun testLowercase() {
assertThat(Petscii.encodePetscii("hello WORLD 123 @!£", true), equalTo( assertThat(Petscii.encodePetscii("hello WORLD 123 @!£", true), equalTo(

View File

@ -6,39 +6,20 @@
~ main { ~ main {
sub start() { sub start() {
c64scr.print_ub(rnd()) uword uw = $ab34
str name = "irmen de jong"
c64scr.print_ub(len(name))
c64.CHROUT('\n') c64.CHROUT('\n')
c64scr.print_ub(rnd()) c64scr.print_ub(strlen(name))
c64.CHROUT('\n') c64.CHROUT('\n')
c64scr.print_ub(rnd()) c64scr.print(name)
c64.CHROUT('\n') c64.CHROUT('\n')
c64scr.print_uw(rndw()) name[6] = 0
c64.CHROUT('\n') c64scr.print_ub(strlen(name))
c64scr.print_uw(rndw())
c64.CHROUT('\n')
c64flt.print_f(rndf())
c64.CHROUT('\n')
c64flt.print_f(rndf())
c64.CHROUT('\n') c64.CHROUT('\n')
c64scr.print(name)
c64.CHROUT('\n') 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')
} }
} }