implemented strlen() function

This commit is contained in:
Irmen de Jong 2019-03-15 23:10:26 +01:00
parent f2397527f1
commit d9d83248fe
9 changed files with 66 additions and 22 deletions

View File

@ -916,7 +916,7 @@ asmsub print_w (word value @ AY) -> clobbers(A,Y) -> () {
}
asmsub input_chars (uword buffer @ AY) -> clobbers(A) -> (ubyte @ Y) {
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y.
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel!
%asm {{

View File

@ -1112,8 +1112,7 @@ _gtequ dey
_result_minw .word 0
.pend
func_len_str .proc
func_strlen .proc
; -- push length of 0-terminated string on stack
jsr peek_address
ldy #0
@ -1126,15 +1125,6 @@ func_len_str .proc
rts
.pend
func_len_strp .proc
; -- push length of pascal-string on stack
jsr peek_address
ldy #0
lda (c64.SCRATCH_ZPWORD1),y ; first byte is length
sta c64.ESTACK_LO+1,x
rts
.pend
func_rnd .proc
; -- put a random ubyte on the estack
jsr math.randbyte

View File

@ -296,7 +296,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
DataType.STR, DataType.STR_S -> {
val rawStr = heap.get(v.second.heapId).str!!
val bytes = encodeStr(rawStr, v.second.type).map { "$" + it.toString(16).padStart(2, '0') }
out("${v.first}\t; ${v.second.type} \"${escape(rawStr)}\"")
out("${v.first}\t; ${v.second.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"")
for (chunk in bytes.chunked(16))
out(" .byte " + chunk.joinToString())
}

View File

@ -1055,11 +1055,12 @@ class Petscii {
val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase
return text.map {
val petscii = lookup[it]
if(petscii==null) {
val case = if(lowercase) "lower" else "upper"
petscii?.toShort() ?: if(it=='\u0000')
0.toShort()
else {
val case = if (lowercase) "lower" else "upper"
throw CharConversionException("no ${case}case Petscii character for '$it'")
}
petscii.toShort()
}
}
@ -1072,11 +1073,12 @@ class Petscii {
val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase
return text.map{
val screencode = lookup[it]
if(screencode==null) {
val case = if(lowercase) "lower" else "upper"
screencode?.toShort() ?: if(it=='\u0000')
0.toShort()
else {
val case = if (lowercase) "lower" else "upper"
throw CharConversionException("no ${case}Screencode character for '$it'")
}
screencode.toShort()
}
}

View File

@ -84,6 +84,7 @@ val BuiltinFunctions = mapOf(
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
BuiltinFunctionParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
"strlen" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.UBYTE, ::builtinStrlen),
"vm_write_memchr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_memstr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_num" to FunctionSignature(false, listOf(BuiltinFunctionParam("number", NumericDatatypes)), null),
@ -302,6 +303,20 @@ private fun builtinAvg(args: List<IExpression>, position: Position, namespace:IN
return numericLiteral(result, args[0].position)
}
private fun builtinStrlen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
if (args.size != 1)
throw SyntaxError("strlen requires one argument", position)
val argument = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
if(argument.type !in StringDatatypes)
throw SyntaxError("strlen must have string argument", position)
val string = argument.strvalue(heap)
val zeroIdx = string.indexOf('\u0000')
return if(zeroIdx>=0)
LiteralValue.optimalInteger(zeroIdx, position=position)
else
LiteralValue.optimalInteger(string.length, position=position)
}
private fun builtinLen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
if(args.size!=1)

View File

@ -51,6 +51,7 @@ enum class Syscall(val callNr: Short) {
FUNC_RNDF(91), // push a random float on the stack (between 0.0 and 1.0)
FUNC_LEN_STR(105),
FUNC_LEN_STRS(106),
FUNC_STRLEN(107),
FUNC_ANY_B(109),
FUNC_ANY_W(110),
FUNC_ANY_F(111),
@ -1736,7 +1737,11 @@ class StackVm(private var traceOutputFile: String?) {
DataType.ARRAY_B -> array.array!![index] = value.integerValue()
DataType.STR, DataType.STR_S -> {
val chars = array.str!!.toCharArray()
chars[index] = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0]
val ps = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0]
if(ps=='\ufffe') // undefined
chars[index] = '\u0000'
else
chars[index] = ps
heap.update(variable.heapId, chars.joinToString(""))
}
else -> throw VmExecutionException("not a proper array/string var with byte elements")
@ -1994,6 +1999,13 @@ class StackVm(private var traceOutputFile: String?) {
val text = heap.get(strPtr).str!!
evalstack.push(Value(DataType.UBYTE, text.length))
}
Syscall.FUNC_STRLEN -> {
val strPtr = evalstack.pop().integerValue()
val text = heap.get(strPtr).str!!
val zeroIdx = text.indexOf('\u0000')
val len = if(zeroIdx>=0) zeroIdx else text.length
evalstack.push(Value(DataType.UBYTE, len))
}
Syscall.FUNC_READ_FLAGS -> {
val carry = if(P_carry) 1 else 0
val zero = if(P_zero) 2 else 0

View File

@ -639,6 +639,11 @@ len(x)
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte.
Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual
length of the string during execution, the value of len(string) may no longer be correct!
(use strlen function if you want to dynamically determine the length)
strlen(str)
Number of bytes in the string. This value is determined during runtime and counts upto
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
lsb(x)
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".

View File

@ -32,7 +32,6 @@
float minutes = floor(clock_seconds / 60)
clock_seconds = floor(clock_seconds - minutes * 60.0)
; @todo implement strcpy/strcat/strlen?
c64scr.print("system time in ti$ is ")
c64flt.print_f(hours)
c64.CHROUT(':')

View File

@ -8,6 +8,27 @@
sub start() {
c64.CLEARSCR() ; @todo empty stack exception in vm
str s1 = "hello\u0000abcd12345"
str_s s2 = "hellothere\u0000bcde"
c64scr.print(s1)
c64.CHROUT('\n')
c64scr.print_ub(len(s1))
c64.CHROUT('\n')
c64scr.print_ub(strlen(s1))
c64.CHROUT('\n')
s1[2]=0
c64scr.print_ub(strlen(s1))
c64.CHROUT('\n')
c64.CHROUT('\n')
c64scr.print_ub(len(s2))
c64.CHROUT('\n')
c64scr.print_ub(strlen(s2))
c64.CHROUT('\n')
s2[7]=0
c64scr.print_ub(strlen(s2))
c64.CHROUT('\n')
}
}