From 2e37f5dee319abb0e26df519fb77829b455261fd Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 23 Mar 2024 00:20:12 +0100 Subject: [PATCH] IR: support for multi-returnvalue function calls (asmsubs) note: the VM can't execute these though as it has no CPU hardware registers --- .../codegen/intermediate/AssignmentGen.kt | 76 ++++++------- .../codegen/intermediate/ExpressionGen.kt | 55 +++++++--- .../prog8/codegen/intermediate/IRCodeGen.kt | 3 +- docs/source/todo.rst | 3 + .../src/prog8/intermediate/IRInstructions.kt | 47 ++++---- intermediate/src/prog8/intermediate/Utils.kt | 18 ++-- virtualmachine/src/prog8/vm/SysCalls.kt | 100 +++++++++--------- virtualmachine/src/prog8/vm/VirtualMachine.kt | 12 +-- 8 files changed, 168 insertions(+), 146 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index 55d19dcb3..646470a3e 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -23,13 +23,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express if(funcCall.multipleResultFpRegs.isNotEmpty()) TODO("deal with (multiple?) FP return registers") - TODO("add to result multi return regs from expression") -// addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg) -// sub.returns.zip(assignment.children).forEach { (returns, target) -> -// result += assignCpuRegister(returns, funcCall, target as PtAssignTarget) -// } -// result.filterIsInstance().firstOrNull()?.appendSrcPosition(assignment.position) -// return result + // because we can only handle integer results right now we can just zip() it all up + addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg) + sub.returns.zip(assignment.children).zip(funcCall.multipleResultRegs).forEach { + val regNumber = it.second + val returns = it.first.first + val target = it.first.second as PtAssignTarget + result += assignCpuRegister(returns, regNumber, target) + } + result.filterIsInstance().firstOrNull()?.appendSrcPosition(assignment.position) + return result } else { if (assignment.target.children.single() is PtIrRegister) throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister") @@ -40,46 +43,31 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express } } - private fun assignCpuRegister(returns: StRomSubParameter, target: PtAssignTarget): IRCodeChunk { - val targetIdentifier = target.identifier - val chunk = IRCodeChunk(null, null) - if(targetIdentifier!=null) { - TODO() - val regNum = 4242 // TODO?? - when(returns.register.registerOrPair) { - RegisterOrPair.A -> chunk += IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum) - RegisterOrPair.X -> chunk += IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum) - RegisterOrPair.Y -> chunk += IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum) - RegisterOrPair.AX -> chunk += IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum) - RegisterOrPair.AY -> chunk += IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum) - RegisterOrPair.XY -> chunk += IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum) - null -> { - when(returns.register.statusflag) { - Statusflag.Pc -> chunk += IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum) - else -> throw AssemblyError("weird statusflag as returnvalue") - } + private fun assignCpuRegister(returns: StRomSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks { + val result = mutableListOf() + val loadCpuRegInstr = when(returns.register.registerOrPair) { + RegisterOrPair.A -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum) + RegisterOrPair.X -> IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum) + RegisterOrPair.Y -> IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum) + RegisterOrPair.AX -> IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum) + RegisterOrPair.AY -> IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum) + RegisterOrPair.XY -> IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum) + null -> { + when(returns.register.statusflag) { + Statusflag.Pc -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum) + else -> throw AssemblyError("weird statusflag as returnvalue") } - else -> throw AssemblyError("cannot load register") } - chunk += IRInstruction(Opcode.STOREM, irType(target.type), reg1=regNum, labelSymbol = targetIdentifier.name) - return chunk + else -> throw AssemblyError("cannot load register") } - val targetMem = target.memory - if(targetMem!=null) { - TODO("assign $returns to $targetMem") - return chunk - } - val targetArray = target.array - if(targetArray!=null) { - TODO("assign $returns to $targetArray") - return chunk - } - throw AssemblyError("weird target") -// val singleAssign = PtAssignment(target.position) -// singleAssign.children.add(target) -// TODO("use the new IR instructions to store machine regs STOREHxx ${target.position}") - // singleAssign.children.add(PtMachineRegister(4242, returns.type, assignment.position)) - // result += translateRegularAssign(singleAssign) + addInstr(result, loadCpuRegInstr, null) + + // build an assignment to store the value in the actual target. + val assign = PtAssignment(target.position) + assign.add(target) + assign.add(PtIrRegister(regNum, target.type, target.position)) + result += translate(assign) + return result } internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 1102e918b..c783409d3 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -466,7 +466,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null))) result += tr.chunks } - // return value + // return value (always singular for normal Subs) val returnRegSpec = if(fcall.void) null else { val returnIrType = irType(callTarget.returnType!!) if(returnIrType==IRDataType.FLOAT) @@ -475,7 +475,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null) } // create the call - addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec)), null) + addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name, + fcallArgs = FunctionCallArgs(argRegisters, if(returnRegSpec==null) emptyList() else listOf(returnRegSpec))), null) return if(fcall.void) ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) else if(fcall.type==DataType.FLOAT) @@ -516,34 +517,37 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } if(callTarget.returns.size>1) - return callWithMultipleReturnValues() + return callRomSubWithMultipleReturnValues(callTarget, fcall, argRegisters, result) - // return a single value - var statusFlagResult: Statusflag? = null + // return a single value (or nothing) val returnRegSpec = if(fcall.void) null else { if(callTarget.returns.isEmpty()) null - val returns = callTarget.returns[0] - val returnIrType = irType(returns.type) - if(returnIrType==IRDataType.FLOAT) - FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register) else { - val returnRegister = codeGen.registers.nextFree() - FunctionCallArgs.RegSpec(returnIrType, returnRegister, returns.register) + val returns = callTarget.returns[0] + val returnIrType = irType(returns.type) + if (returnIrType == IRDataType.FLOAT) + FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register) + else { + val returnRegister = codeGen.registers.nextFree() + FunctionCallArgs.RegSpec(returnIrType, returnRegister, returns.register) + } } } // create the call + val returnRegs = if(returnRegSpec==null) emptyList() else listOf(returnRegSpec) val call = if(callTarget.address==null) - IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec)) + IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegs)) else - IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec)) + IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegs)) addInstr(result, call, null) var finalReturnRegister = returnRegSpec?.registerNum ?: -1 if(fcall.parent is PtAssignment || fcall.parent is PtTypeCast) { // look if the status flag bit should actually be returned as a 0/1 byte value in a result register (so it can be assigned) - if(statusFlagResult!=null && returnRegSpec!=null) { + val statusFlagResult = returnRegSpec?.cpuRegister?.statusflag + if(statusFlagResult!=null) { // assign status flag bit to the return value register finalReturnRegister = returnRegSpec.registerNum if(finalReturnRegister<0) @@ -586,8 +590,27 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } } - private fun callWithMultipleReturnValues(): ExpressionCodeResult { - TODO("call with multiple return values") + private fun callRomSubWithMultipleReturnValues( + callTarget: StRomSub, + fcall: PtFunctionCall, + argRegisters: MutableList, + result: MutableList + ): ExpressionCodeResult { + // return multiple values + val returnRegisters = callTarget.returns.map { + val regnum = if(it.type==DataType.FLOAT) codeGen.registers.nextFreeFloat() else codeGen.registers.nextFree() + FunctionCallArgs.RegSpec(irType(it.type), regnum, it.register) + } + // create the call + val call = + if(callTarget.address==null) + IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)) + else + IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)) + addInstr(result, call, null) + val resultRegs = returnRegisters.filter{it.dt!=IRDataType.FLOAT}.map{it.registerNum} + val resultFpRegs = returnRegisters.filter{it.dt==IRDataType.FLOAT}.map{it.registerNum} + return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1, resultRegs, resultFpRegs) } private fun operatorGreaterThan( diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 192603a8b..0684086d7 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -1650,7 +1650,8 @@ class IRCodeGen( val args = params.map { (dt, reg)-> FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null)) } - val returnSpec = if(returns==null) null else FunctionCallArgs.RegSpec(returns.first, returns.second, null) + // for now, syscalls have 0 or 1 return value + val returnSpec = if(returns==null) emptyList() else listOf(FunctionCallArgs.RegSpec(returns.first, returns.second, null)) it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number, fcallArgs = FunctionCallArgs(args, returnSpec)) } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index e7e48bad0..66ee9a95b 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,6 +1,9 @@ TODO ==== +6502 codegen: make multi return value asmsub calls work. + + ... diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 8372b284e..87e55f135 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -777,7 +777,7 @@ val instructionFormats = mutableMapOf( class FunctionCallArgs( var arguments: List, - val returns: RegSpec? + val returns: List ) { class RegSpec(val dt: IRDataType, val registerNum: Int, val cpuRegister: RegisterOrStatusflag?) class ArgumentSpec(val name: String, val address: Int?, val reg: RegSpec) { @@ -975,13 +975,13 @@ data class IRInstruction( } if(fcallArgs!=null) { - fcallArgs.returns?.let { - if(it.dt==IRDataType.FLOAT) - writeFpRegsCounts[it.registerNum] = writeFpRegsCounts.getValue(it.registerNum)+1 + fcallArgs.returns.forEach { + if (it.dt == IRDataType.FLOAT) + writeFpRegsCounts[it.registerNum] = writeFpRegsCounts.getValue(it.registerNum) + 1 else { writeRegsCounts[it.registerNum] = writeRegsCounts.getValue(it.registerNum) + 1 val types = regsTypes[it.registerNum] - if(types==null) { + if (types == null) { regsTypes[it.registerNum] = mutableSetOf(it.dt) } else { types += it.dt @@ -1049,27 +1049,30 @@ data class IRInstruction( } result.add(")") val returns = fcallArgs.returns - if(returns!=null) { + if(returns.isNotEmpty()) { result.add(":") - val cpuReg = if(returns.cpuRegister==null) "" else { - if(returns.cpuRegister.registerOrPair!=null) - returns.cpuRegister.registerOrPair.toString() - else - returns.cpuRegister.statusflag.toString() - } - if(cpuReg.isEmpty()) { - when (returns.dt) { - IRDataType.BYTE -> result.add("r${returns.registerNum}.b") - IRDataType.WORD -> result.add("r${returns.registerNum}.w") - IRDataType.FLOAT -> result.add("fr${returns.registerNum}.f") + val resultParts = returns.map { returnspec -> + val cpuReg = if (returnspec.cpuRegister == null) "" else { + if (returnspec.cpuRegister.registerOrPair != null) + returnspec.cpuRegister.registerOrPair.toString() + else + returnspec.cpuRegister.statusflag.toString() } - } else { - when(returns.dt) { - IRDataType.BYTE -> result.add("r${returns.registerNum}.b@" + cpuReg) - IRDataType.WORD -> result.add("r${returns.registerNum}.w@" + cpuReg) - IRDataType.FLOAT -> result.add("r${returns.registerNum}.f@" + cpuReg) + if (cpuReg.isEmpty()) { + when (returnspec.dt) { + IRDataType.BYTE -> "r${returnspec.registerNum}.b" + IRDataType.WORD -> "r${returnspec.registerNum}.w" + IRDataType.FLOAT -> "fr${returnspec.registerNum}.f" + } + } else { + when (returnspec.dt) { + IRDataType.BYTE -> "r${returnspec.registerNum}.b@" + cpuReg + IRDataType.WORD -> "r${returnspec.registerNum}.w@" + cpuReg + IRDataType.FLOAT -> "r${returnspec.registerNum}.f@" + cpuReg + } } } + result.add(resultParts.joinToString(",")) } } else { diff --git a/intermediate/src/prog8/intermediate/Utils.kt b/intermediate/src/prog8/intermediate/Utils.kt index 47dc3ff67..f8e8ddf6f 100644 --- a/intermediate/src/prog8/intermediate/Utils.kt +++ b/intermediate/src/prog8/intermediate/Utils.kt @@ -216,7 +216,7 @@ private class ParsedCall( val target: String?, val address: Int?, val args: List, - val returns: FunctionCallArgs.RegSpec? + val returns: List ) private fun parseCall(rest: String): ParsedCall { @@ -239,11 +239,15 @@ private fun parseCall(rest: String): ParsedCall { return FunctionCallArgs.RegSpec(type, num, cpuRegister) } - fun parseReturnRegspec(reg: String): FunctionCallArgs.RegSpec { - return if(reg.startsWith('@')) { - FunctionCallArgs.RegSpec(IRDataType.BYTE, -1, parseRegisterOrStatusflag(reg.drop(1))) - } else { - parseRegspec(reg) + fun parseReturnRegspec(regs: String?): List { + if(regs==null) + return emptyList() + return regs.split(',').map { reg-> + if (reg.startsWith('@')) { + FunctionCallArgs.RegSpec(IRDataType.BYTE, -1, parseRegisterOrStatusflag(reg.drop(1))) + } else { + parseRegspec(reg) + } } } @@ -278,7 +282,7 @@ private fun parseCall(rest: String): ParsedCall { actualTarget, address, arguments, - if(returns==null) null else parseReturnRegspec(returns) + parseReturnRegspec(returns) ) } diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index e5542b5be..a91dc5c8c 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -216,7 +216,7 @@ object SysCalls { if(maxlenvalue>0) input = input.substring(0, min(input.length, maxlenvalue)) vm.memory.setString((address as UShort).toInt(), input, true) - returnValue(callspec.returns!!, input.length, vm) + returnValue(callspec.returns.single(), input.length, vm) } Syscall.SLEEP -> { val duration = getArgValues(callspec.arguments, vm).single() as UShort @@ -237,7 +237,7 @@ object SysCalls { Syscall.GFX_GETPIXEL -> { val (x,y) = getArgValues(callspec.arguments, vm) val color = vm.gfx_getpixel(x as UShort, y as UShort) - returnValue(callspec.returns!!, color, vm) + returnValue(callspec.returns.single(), color, vm) } Syscall.WAIT -> { val time = getArgValues(callspec.arguments, vm).single() as UShort @@ -328,9 +328,9 @@ object SysCalls { 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!!, 1, vm) + returnValue(callspec.returns.single(), 1, vm) else - returnValue(callspec.returns!!, 0, vm) + returnValue(callspec.returns.single(), 0, vm) } Syscall.ANY_WORD -> { val (addressV, lengthV) = getArgValues(callspec.arguments, vm) @@ -339,9 +339,9 @@ object SysCalls { 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!!, 1, vm) + returnValue(callspec.returns.single(), 1, vm) else - returnValue(callspec.returns!!, 0, vm) + returnValue(callspec.returns.single(), 0, vm) } Syscall.ANY_FLOAT -> { val (addressV, lengthV) = getArgValues(callspec.arguments, vm) @@ -350,9 +350,9 @@ object SysCalls { 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!!, 1, vm) + returnValue(callspec.returns.single(), 1, vm) else - returnValue(callspec.returns!!, 0, vm) + returnValue(callspec.returns.single(), 0, vm) } Syscall.ALL_BYTE -> { val (addressV, lengthV) = getArgValues(callspec.arguments, vm) @@ -361,9 +361,9 @@ object SysCalls { 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!!, 1, vm) + returnValue(callspec.returns.single(), 1, vm) else - returnValue(callspec.returns!!, 0, vm) + returnValue(callspec.returns.single(), 0, vm) } Syscall.ALL_WORD -> { val (addressV, lengthV) = getArgValues(callspec.arguments, vm) @@ -372,9 +372,9 @@ object SysCalls { 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!!, 1, vm) + returnValue(callspec.returns.single(), 1, vm) else - returnValue(callspec.returns!!, 0, vm) + returnValue(callspec.returns.single(), 0, vm) } Syscall.ALL_FLOAT -> { val (addressV, lengthV) = getArgValues(callspec.arguments, vm) @@ -383,9 +383,9 @@ object SysCalls { 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!!, 1, vm) + returnValue(callspec.returns.single(), 1, vm) else - returnValue(callspec.returns!!, 0, vm) + returnValue(callspec.returns.single(), 0, vm) } Syscall.PRINT_F -> { val value = getArgValues(callspec.arguments, vm).single() as Double @@ -402,18 +402,18 @@ object SysCalls { } catch(_: NumberFormatException) { 0u } - returnValue(callspec.returns!!, value, vm) + 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!!, 0, vm) + 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!!, value, vm) + return returnValue(callspec.returns.single(), value, vm) } Syscall.STR_TO_FLOAT -> { val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort @@ -428,7 +428,7 @@ object SysCalls { 0.0 } } - returnValue(callspec.returns!!, result, vm) + returnValue(callspec.returns.single(), result, vm) } Syscall.COMPARE_STRINGS -> { val (firstV, secondV) = getArgValues(callspec.arguments, vm) @@ -438,11 +438,11 @@ object SysCalls { val second = vm.memory.getString(secondAddr.toInt()) val comparison = first.compareTo(second) if(comparison==0) - returnValue(callspec.returns!!, 0, vm) + returnValue(callspec.returns.single(), 0, vm) else if(comparison<0) - returnValue(callspec.returns!!, -1, vm) + returnValue(callspec.returns.single(), -1, vm) else - returnValue(callspec.returns!!, 1, vm) + returnValue(callspec.returns.single(), 1, vm) } Syscall.RNDFSEED -> { val seed = getArgValues(callspec.arguments, vm).single() as Double @@ -456,20 +456,20 @@ object SysCalls { vm.randomSeed(seed1 as UShort, seed2 as UShort) } Syscall.RND -> { - returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUByte(), vm) + returnValue(callspec.returns.single(), vm.randomGenerator.nextInt().toUByte(), vm) } Syscall.RNDW -> { - returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUShort(), vm) + returnValue(callspec.returns.single(), vm.randomGenerator.nextInt().toUShort(), vm) } Syscall.RNDF -> { - returnValue(callspec.returns!!, vm.randomGeneratorFloats.nextFloat(), vm) + 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!!, if(char in string) 1u else 0u, vm) + returnValue(callspec.returns.single(), if(char in string) 1u else 0u, vm) } Syscall.BYTEARRAY_CONTAINS -> { val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm) @@ -477,11 +477,11 @@ object SysCalls { var array = (arrayV as UShort).toInt() while(length>0u) { if(vm.memory.getUB(array)==value) - return returnValue(callspec.returns!!, 1u, vm) + return returnValue(callspec.returns.single(), 1u, vm) array++ length-- } - returnValue(callspec.returns!!, 0u, vm) + returnValue(callspec.returns.single(), 0u, vm) } Syscall.WORDARRAY_CONTAINS -> { val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm) @@ -489,11 +489,11 @@ object SysCalls { var array = (arrayV as UShort).toInt() while(length>0u) { if(vm.memory.getUW(array)==value) - return returnValue(callspec.returns!!, 1u, vm) + return returnValue(callspec.returns.single(), 1u, vm) array += 2 length-- } - returnValue(callspec.returns!!, 0u, vm) + returnValue(callspec.returns.single(), 0u, vm) } Syscall.FLOATARRAY_CONTAINS -> { val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm) @@ -501,11 +501,11 @@ object SysCalls { var array = (arrayV as UShort).toInt() while(length>0u) { if(vm.memory.getFloat(array)==value) - return returnValue(callspec.returns!!, 1u, vm) + return returnValue(callspec.returns.single(), 1u, vm) array += vm.machinedef.FLOAT_MEM_SIZE length-- } - returnValue(callspec.returns!!, 0u, vm) + returnValue(callspec.returns.single(), 0u, vm) } Syscall.CLAMP_BYTE -> { val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm) @@ -513,7 +513,7 @@ object SysCalls { val minimum = (minimumU as UByte).toByte().toInt() val maximum = (maximumU as UByte).toByte().toInt() val result = min(max(value, minimum), maximum) - returnValue(callspec.returns!!, result, vm) + returnValue(callspec.returns.single(), result, vm) } Syscall.CLAMP_UBYTE -> { val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm) @@ -521,7 +521,7 @@ object SysCalls { val minimum = (minimumU as UByte).toInt() val maximum = (maximumU as UByte).toInt() val result = min(max(value, minimum), maximum) - returnValue(callspec.returns!!, result, vm) + returnValue(callspec.returns.single(), result, vm) } Syscall.CLAMP_WORD -> { val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm) @@ -529,7 +529,7 @@ object SysCalls { val minimum = (minimumU as UShort).toShort().toInt() val maximum = (maximumU as UShort).toShort().toInt() val result = min(max(value, minimum), maximum) - returnValue(callspec.returns!!, result, vm) + returnValue(callspec.returns.single(), result, vm) } Syscall.CLAMP_UWORD -> { val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm) @@ -537,7 +537,7 @@ object SysCalls { val minimum = (minimumU as UShort).toInt() val maximum = (maximumU as UShort).toInt() val result = min(max(value, minimum), maximum) - returnValue(callspec.returns!!, result, vm) + returnValue(callspec.returns.single(), result, vm) } Syscall.CLAMP_FLOAT -> { val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm) @@ -545,7 +545,7 @@ object SysCalls { val minimum = minimumU as Double val maximum = maximumU as Double val result = min(max(value, minimum), maximum) - returnValue(callspec.returns!!, result, vm) + returnValue(callspec.returns.single(), result, vm) } Syscall.ATAN -> { val (x1, y1, x2, y2) = getArgValues(callspec.arguments, vm) @@ -557,10 +557,10 @@ object SysCalls { if(radians<0) radians+=2*PI val result = floor(radians/2.0/PI*256.0) - returnValue(callspec.returns!!, result, vm) + returnValue(callspec.returns.single(), result, vm) } Syscall.MUL16_LAST_UPPER -> { - returnValue(callspec.returns!!, vm.mul16_last_upper, vm) + returnValue(callspec.returns.single(), vm.mul16_last_upper, vm) } Syscall.FLOAT_TO_STR -> { val (buffer, number) = getArgValues(callspec.arguments, vm) @@ -612,7 +612,7 @@ object SysCalls { val target = (targetA as UShort).toInt() val string = vm.memory.getString(source) vm.memory.setString(target, string, true) - returnValue(callspec.returns!!, string.length, vm) + returnValue(callspec.returns.single(), string.length, vm) } Syscall.ARRAYCOPY_SPLITW_TO_NORMAL -> { val (fromLsbA, fromMsbA, targetA, bytecountA) = getArgValues(callspec.arguments, vm) @@ -646,9 +646,9 @@ object SysCalls { for (i in 0.. { @@ -660,9 +660,9 @@ object SysCalls { for (i in 0.. { @@ -688,10 +688,10 @@ object SysCalls { } val filename = vm.memory.getString((filenamePtr as UShort).toInt()) if (File(filename).exists()) - returnValue(callspec.returns!!, 0u, vm) + returnValue(callspec.returns.single(), 0u, vm) else { File(filename).writeBytes(data) - returnValue(callspec.returns!!, 1u, vm) + returnValue(callspec.returns.single(), 1u, vm) } } Syscall.DELETE -> { @@ -712,12 +712,12 @@ object SysCalls { directory.listDirectoryEntries().sorted().forEach { println("${it.toFile().length()}\t${it.normalize()}") } - returnValue(callspec.returns!!, 1u, vm) + returnValue(callspec.returns.single(), 1u, vm) } Syscall.GETGONSOLESIZE -> { // no arguments if(System.console()==null) { - return returnValue(callspec.returns!!, 30*256 + 80, vm) // just return some defaults in this case 80*30 + return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30 } val linesS = System.getenv("LINES") @@ -725,7 +725,7 @@ object SysCalls { if(linesS!=null && columnsS!=null) { val lines = linesS.toInt() val columns = columnsS.toInt() - return returnValue(callspec.returns!!, lines*256 + columns, vm) + return returnValue(callspec.returns.single(), lines*256 + columns, vm) } try { @@ -735,12 +735,12 @@ object SysCalls { val response = process.inputStream.bufferedReader().lineSequence().iterator() val width = response.next().toInt() val height = response.next().toInt() - return returnValue(callspec.returns!!, height*256 + width, vm) + return returnValue(callspec.returns.single(), height*256 + width, vm) } } catch (x: Exception) { // dunno what happened... } - return returnValue(callspec.returns!!, 30*256 + 80, vm) // just return some defaults in this case 80*30 + return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30 } } } diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index b34f56a52..61db11dbc 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -655,8 +655,8 @@ class VirtualMachine(irProgram: IRProgram) { val returns = context.fcallSpec.returns when (i.type!!) { IRDataType.BYTE -> { - if(returns!=null) - registers.setUB(returns.registerNum, registers.getUB(i.reg1!!)) + if(returns.isNotEmpty()) + registers.setUB(returns.single().registerNum, registers.getUB(i.reg1!!)) else { val callInstr = context.returnChunk.instructions[context.returnIndex-1] if(callInstr.opcode!=Opcode.CALL) @@ -664,8 +664,8 @@ class VirtualMachine(irProgram: IRProgram) { } } IRDataType.WORD -> { - if(returns!=null) - registers.setUW(returns.registerNum, registers.getUW(i.reg1!!)) + if(returns.isNotEmpty()) + registers.setUW(returns.single().registerNum, registers.getUW(i.reg1!!)) else { val callInstr = context.returnChunk.instructions[context.returnIndex-1] if(callInstr.opcode!=Opcode.CALL) @@ -673,8 +673,8 @@ class VirtualMachine(irProgram: IRProgram) { } } IRDataType.FLOAT -> { - if(returns!=null) - registers.setFloat(returns.registerNum, registers.getFloat(i.fpReg1!!)) + if(returns.isNotEmpty()) + registers.setFloat(returns.single().registerNum, registers.getFloat(i.fpReg1!!)) else { val callInstr = context.returnChunk.instructions[context.returnIndex-1] if(callInstr.opcode!=Opcode.CALL)