prog8/virtualmachine/src/prog8/vm/SysCalls.kt

748 lines
32 KiB
Kotlin

package prog8.vm
import prog8.intermediate.FunctionCallArgs
import prog8.intermediate.IRDataType
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.listDirectoryEntries
import kotlin.math.*
/*
SYSCALLS:
0 = reset ; resets system
1 = exit ; stops program and returns statuscode from r0.w
2 = print_c ; print single character
3 = print_s ; print 0-terminated string from memory
4 = print_u8 ; print unsigned int byte
5 = print_u16 ; print unsigned int word
6 = input ; reads a line of text entered by the user, r0.w = memory buffer, r1.b = maxlength (0-255, 0=unlimited). Zero-terminates the string. Returns length in r0.w
7 = sleep ; sleep amount of milliseconds
8 = gfx_enable ; enable graphics window r0.b = 0 -> lores 320x240, r0.b = 1 -> hires 640x480
9 = gfx_clear ; clear graphics window with shade in r0.b
10 = gfx_plot ; plot pixel in graphics window, r0.w/r1.w contain X and Y coordinates, r2.b contains brightness
11 = decimal string to word (unsigned)
12 = decimal string to word (signed)
13 = wait ; wait certain amount of jiffies (1/60 sec)
14 = waitvsync ; wait on vsync
15 = sort_ubyte array
16 = sort_byte array
17 = sort_uword array
18 = sort_word array
19 = any_byte array
20 = any_word array
21 = any_float array
22 = all_byte array
23 = all_word array
24 = all_float array
25 = print_f (floating point value in fp reg 0)
26 = reverse_bytes array
27 = reverse_words array
28 = reverse_floats array
29 = compare strings
30 = gfx_getpixel ; get byte pixel value at coordinates r0.w/r1.w
31 = rndseed
32 = rndfseed
33 = RND
34 = RNDW
35 = RNDF
36 = STRING_CONTAINS
37 = BYTEARRAY_CONTAINS
38 = WORDARRAY_CONTAINS
39 = CLAMP_BYTE
40 = CLAMP_UBYTE
41 = CLAMP_WORD
42 = CLAMP_UWORD
43 = CLAMP_FLOAT
44 = ATAN
45 = str to float
46 = MUL16_LAST_UPPER
47 = float to str
48 = FLOATARRAY_CONTAINS
49 = memcopy
50 = memset
51 = memsetw
52 = stringcopy
53 = ARRAYCOPY_SPLITW_TO_NORMAL
54 = ARRAYCOPY_NORMAL_TO_SPLITW
55 = memcopy_small
56 = load
57 = load_raw
58 = save
59 = delete
60 = rename
61 = directory
62 = getconsolesize
*/
enum class Syscall {
RESET,
EXIT,
PRINT_C,
PRINT_S,
PRINT_U8,
PRINT_U16,
INPUT,
SLEEP,
GFX_ENABLE,
GFX_CLEAR,
GFX_PLOT,
STR_TO_UWORD,
STR_TO_WORD,
WAIT,
WAITVSYNC,
SORT_UBYTE,
SORT_BYTE,
SORT_UWORD,
SORT_WORD,
ANY_BYTE,
ANY_WORD,
ANY_FLOAT,
ALL_BYTE,
ALL_WORD,
ALL_FLOAT,
PRINT_F,
REVERSE_BYTES,
REVERSE_WORDS,
REVERSE_FLOATS,
COMPARE_STRINGS,
GFX_GETPIXEL,
RNDSEED,
RNDFSEED,
RND,
RNDW,
RNDF,
STRING_CONTAINS,
BYTEARRAY_CONTAINS,
WORDARRAY_CONTAINS,
CLAMP_BYTE,
CLAMP_UBYTE,
CLAMP_WORD,
CLAMP_UWORD,
CLAMP_FLOAT,
ATAN,
STR_TO_FLOAT,
MUL16_LAST_UPPER,
FLOAT_TO_STR,
FLOATARRAY_CONTAINS,
MEMCOPY,
MEMSET,
MEMSETW,
STRINGCOPY,
ARRAYCOPY_SPLITW_TO_NORMAL,
ARRAYCOPY_NORMAL_TO_SPLITW,
MEMCOPY_SMALL,
LOAD,
LOAD_RAW,
SAVE,
DELETE,
RENAME,
DIRECTORY,
GETGONSOLESIZE
;
companion object {
private val VALUES = values()
fun fromInt(value: Int) = VALUES[value]
}
}
object SysCalls {
private fun getArgValues(argspec: List<FunctionCallArgs.ArgumentSpec>, vm: VirtualMachine): List<Comparable<Nothing>> {
return argspec.map {
when(it.reg.dt) {
IRDataType.BYTE -> vm.registers.getUB(it.reg.registerNum)
IRDataType.WORD -> vm.registers.getUW(it.reg.registerNum)
IRDataType.FLOAT -> vm.registers.getFloat(it.reg.registerNum)
}
}
}
private fun returnValue(returns: FunctionCallArgs.RegSpec, value: Comparable<Nothing>, vm: VirtualMachine) {
val vv: Double = when(value) {
is UByte -> value.toDouble()
is UShort -> value.toDouble()
is UInt -> value.toDouble()
is Byte -> value.toDouble()
is Short -> value.toDouble()
is Int -> value.toDouble()
is Float -> value.toDouble()
is Double -> value
else -> (value as Number).toDouble()
}
when(returns.dt) {
IRDataType.BYTE -> vm.registers.setUB(returns.registerNum, vv.toInt().toUByte())
IRDataType.WORD -> vm.registers.setUW(returns.registerNum, vv.toInt().toUShort())
IRDataType.FLOAT -> vm.registers.setFloat(returns.registerNum, vv)
}
}
fun call(call: Syscall, callspec: FunctionCallArgs, vm: VirtualMachine) {
when(call) {
Syscall.RESET -> {
vm.reset(false)
}
Syscall.EXIT ->{
val exitValue = getArgValues(callspec.arguments, vm).single() as UByte
vm.exit(exitValue.toInt())
}
Syscall.PRINT_C -> {
val char = getArgValues(callspec.arguments, vm).single() as UByte
print(Char(char.toInt()))
}
Syscall.PRINT_S -> {
var addr = (getArgValues(callspec.arguments, vm).single() as UShort).toInt()
while(true) {
val char = vm.memory.getUB(addr).toInt()
if(char==0)
break
print(Char(char))
addr++
}
}
Syscall.PRINT_U8 -> {
val value = getArgValues(callspec.arguments, vm).single()
print(value)
}
Syscall.PRINT_U16 -> {
val value = getArgValues(callspec.arguments, vm).single()
print(value)
}
Syscall.INPUT -> {
val (address, maxlen) = getArgValues(callspec.arguments, vm)
var input = readln()
val maxlenvalue = (maxlen as UByte).toInt()
if(maxlenvalue>0)
input = input.substring(0, min(input.length, maxlenvalue))
vm.memory.setString((address as UShort).toInt(), input, true)
returnValue(callspec.returns.single(), input.length, vm)
}
Syscall.SLEEP -> {
val duration = getArgValues(callspec.arguments, vm).single() as UShort
Thread.sleep(duration.toLong())
}
Syscall.GFX_ENABLE -> {
val mode = getArgValues(callspec.arguments, vm).single() as UByte
vm.gfx_enable(mode)
}
Syscall.GFX_CLEAR -> {
val color = getArgValues(callspec.arguments, vm).single() as UByte
vm.gfx_clear(color)
}
Syscall.GFX_PLOT -> {
val (x,y,color) = getArgValues(callspec.arguments, vm)
vm.gfx_plot(x as UShort, y as UShort, color as UByte)
}
Syscall.GFX_GETPIXEL -> {
val (x,y) = getArgValues(callspec.arguments, vm)
val color = vm.gfx_getpixel(x as UShort, y as UShort)
returnValue(callspec.returns.single(), color, vm)
}
Syscall.WAIT -> {
val time = getArgValues(callspec.arguments, vm).single() as UShort
Thread.sleep(time.toLong() * 1000/60)
}
Syscall.WAITVSYNC -> vm.waitvsync()
Syscall.SORT_UBYTE -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it)
}.sorted()
array.withIndex().forEach { (index, value)->
vm.memory.setUB(address+index, value)
}
}
Syscall.SORT_BYTE -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getSB(it)
}.sorted()
array.withIndex().forEach { (index, value)->
vm.memory.setSB(address+index, value)
}
}
Syscall.SORT_UWORD -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it)
}.sorted()
array.withIndex().forEach { (index, value)->
vm.memory.setUW(address+index*2, value)
}
}
Syscall.SORT_WORD -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getSW(it)
}.sorted()
array.withIndex().forEach { (index, value)->
vm.memory.setSW(address+index*2, value)
}
}
Syscall.REVERSE_BYTES -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it)
}.reversed()
array.withIndex().forEach { (index, value)->
vm.memory.setUB(address+index, value)
}
}
Syscall.REVERSE_WORDS -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it)
}.reversed()
array.withIndex().forEach { (index, value)->
vm.memory.setUW(address+index*2, value)
}
}
Syscall.REVERSE_FLOATS -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map {
vm.memory.getFloat(it)
}.reversed()
array.withIndex().forEach { (index, value)->
vm.memory.setFloat(address+index*4, value)
}
}
Syscall.ANY_BYTE -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + if(length==0) 256 else length
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-1, 1)
if(addresses.any { vm.memory.getUB(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ANY_WORD -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + if(length==0) 256*2 else length*2
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-2, 2)
if(addresses.any { vm.memory.getUW(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ANY_FLOAT -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + (if(length==0) 256*vm.machinedef.FLOAT_MEM_SIZE else length*vm.machinedef.FLOAT_MEM_SIZE)
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-vm.machinedef.FLOAT_MEM_SIZE, 4)
if(addresses.any { vm.memory.getFloat(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ALL_BYTE -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + if(length==0) 256 else length
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-1, 1)
if(addresses.all { vm.memory.getUB(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ALL_WORD -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + if(length==0) 256*2 else length*2
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-2, 2)
if(addresses.all { vm.memory.getUW(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ALL_FLOAT -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + (if(length==0) 256*vm.machinedef.FLOAT_MEM_SIZE else length*vm.machinedef.FLOAT_MEM_SIZE)
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-vm.machinedef.FLOAT_MEM_SIZE, 4)
if(addresses.all { vm.memory.getFloat(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.PRINT_F -> {
val value = getArgValues(callspec.arguments, vm).single() as Double
if(value.toInt().toDouble()==value)
print(value.toInt())
else
print(value)
}
Syscall.STR_TO_UWORD -> {
val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
val value = try {
string.toUShort()
} catch(_: NumberFormatException) {
0u
}
returnValue(callspec.returns.single(), value, vm)
}
Syscall.STR_TO_WORD -> {
val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
val memstring = vm.memory.getString(stringAddr.toInt())
val match = Regex("^[+-]?\\d+").find(memstring) ?: return returnValue(callspec.returns.single(), 0, vm)
val value = try {
match.value.toShort()
} catch(_: NumberFormatException) {
0
}
return returnValue(callspec.returns.single(), value, vm)
}
Syscall.STR_TO_FLOAT -> {
val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
val memstring = vm.memory.getString(stringAddr.toInt()).replace(" ", "")
val result = if(memstring.isEmpty())
0.0
else {
val trimmed = memstring.takeWhile { it in " +-0123456789.eE" }
try {
trimmed.toDouble()
} catch(x: NumberFormatException) {
0.0
}
}
returnValue(callspec.returns.single(), result, vm)
}
Syscall.COMPARE_STRINGS -> {
val (firstV, secondV) = getArgValues(callspec.arguments, vm)
val firstAddr = firstV as UShort
val secondAddr = secondV as UShort
val first = vm.memory.getString(firstAddr.toInt())
val second = vm.memory.getString(secondAddr.toInt())
val comparison = first.compareTo(second)
if(comparison==0)
returnValue(callspec.returns.single(), 0, vm)
else if(comparison<0)
returnValue(callspec.returns.single(), -1, vm)
else
returnValue(callspec.returns.single(), 1, vm)
}
Syscall.RNDFSEED -> {
val seed = getArgValues(callspec.arguments, vm).single() as Double
if(seed>0) // always use negative seed, this mimics the behavior on CBM machines
vm.randomSeedFloat(-seed)
else
vm.randomSeedFloat(seed)
}
Syscall.RNDSEED -> {
val (seed1, seed2) = getArgValues(callspec.arguments, vm)
vm.randomSeed(seed1 as UShort, seed2 as UShort)
}
Syscall.RND -> {
returnValue(callspec.returns.single(), vm.randomGenerator.nextInt().toUByte(), vm)
}
Syscall.RNDW -> {
returnValue(callspec.returns.single(), vm.randomGenerator.nextInt().toUShort(), vm)
}
Syscall.RNDF -> {
returnValue(callspec.returns.single(), vm.randomGeneratorFloats.nextFloat(), vm)
}
Syscall.STRING_CONTAINS -> {
val (charV, addr) = getArgValues(callspec.arguments, vm)
val stringAddr = addr as UShort
val char = (charV as UByte).toInt().toChar()
val string = vm.memory.getString(stringAddr.toInt())
returnValue(callspec.returns.single(), if(char in string) 1u else 0u, vm)
}
Syscall.BYTEARRAY_CONTAINS -> {
val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
var length = lengthV as UByte
var array = (arrayV as UShort).toInt()
while(length>0u) {
if(vm.memory.getUB(array)==value)
return returnValue(callspec.returns.single(), 1u, vm)
array++
length--
}
returnValue(callspec.returns.single(), 0u, vm)
}
Syscall.WORDARRAY_CONTAINS -> {
val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
var length = lengthV as UByte
var array = (arrayV as UShort).toInt()
while(length>0u) {
if(vm.memory.getUW(array)==value)
return returnValue(callspec.returns.single(), 1u, vm)
array += 2
length--
}
returnValue(callspec.returns.single(), 0u, vm)
}
Syscall.FLOATARRAY_CONTAINS -> {
val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
var length = lengthV as UByte
var array = (arrayV as UShort).toInt()
while(length>0u) {
if(vm.memory.getFloat(array)==value)
return returnValue(callspec.returns.single(), 1u, vm)
array += vm.machinedef.FLOAT_MEM_SIZE
length--
}
returnValue(callspec.returns.single(), 0u, vm)
}
Syscall.CLAMP_BYTE -> {
val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm)
val value = (valueU as UByte).toByte().toInt()
val minimum = (minimumU as UByte).toByte().toInt()
val maximum = (maximumU as UByte).toByte().toInt()
val result = min(max(value, minimum), maximum)
returnValue(callspec.returns.single(), result, vm)
}
Syscall.CLAMP_UBYTE -> {
val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm)
val value = (valueU as UByte).toInt()
val minimum = (minimumU as UByte).toInt()
val maximum = (maximumU as UByte).toInt()
val result = min(max(value, minimum), maximum)
returnValue(callspec.returns.single(), result, vm)
}
Syscall.CLAMP_WORD -> {
val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm)
val value = (valueU as UShort).toShort().toInt()
val minimum = (minimumU as UShort).toShort().toInt()
val maximum = (maximumU as UShort).toShort().toInt()
val result = min(max(value, minimum), maximum)
returnValue(callspec.returns.single(), result, vm)
}
Syscall.CLAMP_UWORD -> {
val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm)
val value = (valueU as UShort).toInt()
val minimum = (minimumU as UShort).toInt()
val maximum = (maximumU as UShort).toInt()
val result = min(max(value, minimum), maximum)
returnValue(callspec.returns.single(), result, vm)
}
Syscall.CLAMP_FLOAT -> {
val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm)
val value = valueU as Double
val minimum = minimumU as Double
val maximum = maximumU as Double
val result = min(max(value, minimum), maximum)
returnValue(callspec.returns.single(), result, vm)
}
Syscall.ATAN -> {
val (x1, y1, x2, y2) = getArgValues(callspec.arguments, vm)
val x1f = (x1 as UByte).toDouble()
val y1f = (y1 as UByte).toDouble()
val x2f = (x2 as UByte).toDouble()
val y2f = (y2 as UByte).toDouble()
var radians = atan2(y2f-y1f, x2f-x1f)
if(radians<0)
radians+=2*PI
val result = floor(radians/2.0/PI*256.0)
returnValue(callspec.returns.single(), result, vm)
}
Syscall.MUL16_LAST_UPPER -> {
returnValue(callspec.returns.single(), vm.mul16_last_upper, vm)
}
Syscall.FLOAT_TO_STR -> {
val (buffer, number) = getArgValues(callspec.arguments, vm)
val bufferAddr = (buffer as UShort).toShort().toInt()
val numf = number as Double
val numStr = if(numf.toInt().toDouble()==numf) numf.toInt().toString() else numf.toString()
vm.memory.setString(bufferAddr, numStr, true)
}
Syscall.MEMCOPY -> {
val (fromA, toA, countA) = getArgValues(callspec.arguments, vm)
val from = (fromA as UShort).toInt()
val to = (toA as UShort).toInt()
val count = (countA as UShort).toInt()
for(offset in 0..<count) {
vm.memory.setUB(to+offset, vm.memory.getUB(from+offset))
}
}
Syscall.MEMCOPY_SMALL -> {
val (fromA, toA, countA) = getArgValues(callspec.arguments, vm)
val from = (fromA as UShort).toInt()
val to = (toA as UShort).toInt()
val countV = (countA as UByte).toInt()
val count = if(countV==0) 256 else countV
for(offset in 0..<count) {
vm.memory.setUB(to+offset, vm.memory.getUB(from+offset))
}
}
Syscall.MEMSET -> {
val (memA, numbytesA, valueA) = getArgValues(callspec.arguments, vm)
val mem = (memA as UShort).toInt()
val numbytes = (numbytesA as UShort).toInt()
val value = valueA as UByte
for(addr in mem..<mem+numbytes) {
vm.memory.setUB(addr, value)
}
}
Syscall.MEMSETW -> {
val (memA, numwordsA, valueA) = getArgValues(callspec.arguments, vm)
val mem = (memA as UShort).toInt()
val numwords = (numwordsA as UShort).toInt()
val value = valueA as UShort
for(addr in mem..<mem+numwords*2 step 2) {
vm.memory.setUW(addr, value)
}
}
Syscall.STRINGCOPY -> {
val (sourceA, targetA) = getArgValues(callspec.arguments, vm)
val source = (sourceA as UShort).toInt()
val target = (targetA as UShort).toInt()
val string = vm.memory.getString(source)
vm.memory.setString(target, string, true)
returnValue(callspec.returns.single(), string.length, vm)
}
Syscall.ARRAYCOPY_SPLITW_TO_NORMAL -> {
val (fromLsbA, fromMsbA, targetA, bytecountA) = getArgValues(callspec.arguments, vm)
val fromLsb = (fromLsbA as UShort).toInt()
val fromMsb = (fromMsbA as UShort).toInt()
val target = (targetA as UShort).toInt()
val bytecount = (bytecountA as UByte).toInt()
for(offset in 0..<bytecount) {
vm.memory.setUB(target+offset*2, vm.memory.getUB(fromLsb+offset))
vm.memory.setUB(target+offset*2+1, vm.memory.getUB(fromMsb+offset))
}
}
Syscall.ARRAYCOPY_NORMAL_TO_SPLITW -> {
val (fromA, targetLsbA, targetMsbA, bytecountA) = getArgValues(callspec.arguments, vm)
val from = (fromA as UShort).toInt()
val targetLsb = (targetLsbA as UShort).toInt()
val targetMsb = (targetMsbA as UShort).toInt()
val bytecount = (bytecountA as UByte).toInt()
for(offset in 0..<bytecount) {
vm.memory.setUB(targetLsb+offset, vm.memory.getUB(from+offset*2))
vm.memory.setUB(targetMsb+offset, vm.memory.getUB(from+offset*2+1))
}
}
Syscall.LOAD -> {
val (filenameA, addrA) = getArgValues(callspec.arguments, vm)
val filename = vm.memory.getString((filenameA as UShort).toInt())
if(File(filename).exists()) {
val data = File(filename).readBytes()
val addr = if (addrA == 0) data[0] + data[1] * 256 else (addrA as UShort).toInt()
for (i in 0..<data.size - 2) {
vm.memory.setUB(addr + i, data[i + 2].toUByte())
}
returnValue(callspec.returns.single(), (addr + data.size - 2).toUShort(), vm)
} else {
returnValue(callspec.returns.single(), 0u, vm)
}
}
Syscall.LOAD_RAW -> {
val (filenameA, addrA) = getArgValues(callspec.arguments, vm)
val filename = vm.memory.getString((filenameA as UShort).toInt())
val addr = (addrA as UShort).toInt()
if(File(filename).exists()) {
val data = File(filename).readBytes()
for (i in 0..<data.size) {
vm.memory.setUB(addr + i, data[i].toUByte())
}
returnValue(callspec.returns.single(), (addr + data.size).toUShort(), vm)
} else {
returnValue(callspec.returns.single(), 0u, vm)
}
}
Syscall.SAVE -> {
val (rawA, filenamePtr, startA, sizeA) = getArgValues(callspec.arguments, vm)
val raw = (rawA as UByte).toInt()
val size = (sizeA as UShort).toInt()
val startPtr = (startA as UShort).toInt()
val data: ByteArray
if(raw==0) {
// save with 2 byte PRG load address header
data = ByteArray(size+2)
data[0] = (startPtr and 255).toByte()
data[1] = (startPtr shr 8).toByte()
for (i in 0..<size) {
data[i + 2] = vm.memory.getUB(startPtr + i).toByte()
}
} else {
// 'raw' save, without header
data = ByteArray(size)
for (i in 0..<size) {
data[i] = vm.memory.getUB(startPtr + i).toByte()
}
}
val filename = vm.memory.getString((filenamePtr as UShort).toInt())
if (File(filename).exists())
returnValue(callspec.returns.single(), 0u, vm)
else {
File(filename).writeBytes(data)
returnValue(callspec.returns.single(), 1u, vm)
}
}
Syscall.DELETE -> {
val filenamePtr = getArgValues(callspec.arguments, vm).single() as UShort
val filename = vm.memory.getString(filenamePtr.toInt())
File(filename).delete()
}
Syscall.RENAME -> {
val (origFilenamePtr, newFilenamePtr) = getArgValues(callspec.arguments, vm)
val origFilename = vm.memory.getString((origFilenamePtr as UShort).toInt())
val newFilename = vm.memory.getString((newFilenamePtr as UShort).toInt())
File(origFilename).renameTo(File(newFilename))
}
Syscall.DIRECTORY -> {
// no arguments
val directory = Path(".")
println("Directory listing for ${directory.toAbsolutePath().normalize()}")
directory.listDirectoryEntries().sorted().forEach {
println("${it.toFile().length()}\t${it.normalize()}")
}
returnValue(callspec.returns.single(), 1u, vm)
}
Syscall.GETGONSOLESIZE -> {
// no arguments
if(System.console()==null) {
return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30
}
val linesS = System.getenv("LINES")
val columnsS = System.getenv("COLUMNS")
if(linesS!=null && columnsS!=null) {
val lines = linesS.toInt()
val columns = columnsS.toInt()
return returnValue(callspec.returns.single(), lines*256 + columns, vm)
}
try {
val process = ProcessBuilder("tput", "cols", "lines").inheritIO().redirectOutput(ProcessBuilder.Redirect.PIPE).start()
val result=process.waitFor()
if (result == 0) {
val response = process.inputStream.bufferedReader().lineSequence().iterator()
val width = response.next().toInt()
val height = response.next().toInt()
return returnValue(callspec.returns.single(), height*256 + width, vm)
}
} catch (x: Exception) {
// dunno what happened...
}
return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30
}
}
}
}