diff --git a/.idea/misc.xml b/.idea/misc.xml index 9d225e1e8..b8d8a3f30 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -22,4 +22,9 @@ + + + \ No newline at end of file diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index 71fa3499f..1d8f54a05 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -2,6 +2,7 @@ package prog8.codegen.virtual import prog8.code.StStaticVariable import prog8.code.ast.* +import prog8.code.core.ArrayToElementTypes import prog8.code.core.AssemblyError import prog8.code.core.DataType import prog8.vm.Opcode @@ -13,10 +14,10 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { return when(call.name) { "max" -> funcMax(call, resultRegister) - "min" -> TODO() - "sum" -> TODO() - "any" -> TODO() - "all" -> TODO() + "min" -> funcMin(call, resultRegister) + "sum" -> funcSum(call, resultRegister) + "any" -> funcAny(call, resultRegister) + "all" -> funcAll(call, resultRegister) "abs" -> TODO("abs once we can compare plus minus") "cmp" -> TODO("cmp() can't be used on vm because no processor status bits implemented") "sgn" -> funcSgn(call, resultRegister) @@ -70,22 +71,113 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: } } - private fun funcMax(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + private fun funcSum(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val arrayName = call.args[0] as PtIdentifier + val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable + val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt)) + val syscall = + when(array.dt) { + DataType.ARRAY_UB, + DataType.ARRAY_B -> Syscall.SUM_BYTE + DataType.ARRAY_UW, + DataType.ARRAY_W -> Syscall.SUM_WORD + DataType.ARRAY_F -> TODO("float sum") + else -> throw IllegalArgumentException("weird type") + } val code = VmCodeChunk() - val arrayName = (call.args.single() as PtIdentifier).targetName - val array = codeGen.symbolTable.flat.getValue(arrayName) as StStaticVariable - when (array.dt) { - DataType.ARRAY_UW, DataType.ARRAY_W -> { - TODO("max word array") - } - DataType.STR -> { - TODO("max string") - } - else -> { - TODO("max byte array") - } - } + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) + code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0) + return code + } + private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val arrayName = call.args[0] as PtIdentifier + val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable + val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt)) + val syscall = + when(array.dt) { + DataType.ARRAY_UB, + DataType.ARRAY_B -> Syscall.ANY_BYTE + DataType.ARRAY_UW, + DataType.ARRAY_W -> Syscall.ANY_WORD + DataType.ARRAY_F -> TODO("float any") + else -> throw IllegalArgumentException("weird type") + } + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) + code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0) + return code + } + + private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val arrayName = call.args[0] as PtIdentifier + val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable + val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt)) + val syscall = + when(array.dt) { + DataType.ARRAY_UB, + DataType.ARRAY_B -> Syscall.ALL_BYTE + DataType.ARRAY_UW, + DataType.ARRAY_W -> Syscall.ALL_WORD + DataType.ARRAY_F -> TODO("float all") + else -> throw IllegalArgumentException("weird type") + } + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) + code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0) + return code + } + + private fun funcMax(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val arrayName = call.args[0] as PtIdentifier + val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable + val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt)) + val syscall = + when(array.dt) { + DataType.ARRAY_UB -> Syscall.MAX_UBYTE + DataType.ARRAY_B -> Syscall.MAX_BYTE + DataType.ARRAY_UW -> Syscall.MAX_UWORD + DataType.ARRAY_W -> Syscall.MAX_WORD + DataType.ARRAY_F -> TODO("float max") + else -> throw IllegalArgumentException("weird type") + } + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) + code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0) + return code + } + + private fun funcMin(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val arrayName = call.args[0] as PtIdentifier + val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable + val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt)) + val syscall = + when(array.dt) { + DataType.ARRAY_UB -> Syscall.MIN_UBYTE + DataType.ARRAY_B -> Syscall.MIN_BYTE + DataType.ARRAY_UW -> Syscall.MIN_UWORD + DataType.ARRAY_W -> Syscall.MIN_WORD + DataType.ARRAY_F -> TODO("float min") + else -> throw IllegalArgumentException("weird type") + } + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) + code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0) return code } @@ -151,7 +243,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: when(array.dt) { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> Syscall.REVERSE_BYTES DataType.ARRAY_UW, DataType.ARRAY_W -> Syscall.REVERSE_WORDS - DataType.FLOAT -> TODO("reverse floats") + DataType.ARRAY_F -> TODO("float reverse") else -> throw IllegalArgumentException("weird type to reverse") } val code = VmCodeChunk() @@ -170,7 +262,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: DataType.ARRAY_B -> Syscall.SORT_BYTE DataType.ARRAY_UW -> Syscall.SORT_UWORD DataType.ARRAY_W -> Syscall.SORT_WORD - DataType.FLOAT -> TODO("float sort") + DataType.ARRAY_F -> TODO("float sort") DataType.STR -> Syscall.SORT_UBYTE else -> throw IllegalArgumentException("weird type to sort") } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index a80165058..e2b5b559f 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1069,11 +1069,15 @@ internal class AstChecker(private val program: Program, errors.err("swap requires args of numerical type", position) } else if(target.name=="all" || target.name=="any") { - if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR) { + if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR + || args[0].inferType(program).getOr(DataType.STR) == DataType.STR) { errors.err("any/all on a string is useless (is always true unless the string is empty)", position) } - if(args[0].inferType(program).getOr(DataType.STR) == DataType.STR) { - errors.err("any/all on a string is useless (is always true unless the string is empty)", position) + } + else if(target.name=="min" || target.name=="max") { + if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR + || args[0].inferType(program).getOr(DataType.STR) == DataType.STR) { + errors.err("min/max operate on arrays, not on strings", position) } } } else if(target is Subroutine) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 7aa010cc2..ffbf70443 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,9 +3,7 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- min/max/any/all should give error when string arg is given instead of array - can't use abs() etc in pipe expression because return type depends on argument type -- vm: add support for all builtin functions - pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls. - createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there. - allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type @@ -14,6 +12,7 @@ For next release If we can do that why not perhaps also able to inline multi-line subroutines? Why would it be limited to just 1 line? Maybe to protect against code size bloat. Inlined subroutines cannot contain further nested subroutines! Once this works, look for library subroutines that should be inlined. +- vm: add support for status bits, status-branch instructions, and cmp() and abs() functions. ... diff --git a/examples/test.p8 b/examples/test.p8 index 35ae2eb42..5ac132075 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -9,8 +9,6 @@ main { word[] values = [1111, -222, -9999, 88, 20222, 0, 0, 1111] word[] values2 = [0,0,0,0,0,1,0,0,0] - txt.print_w(max("abcde")) - txt.nl() txt.print_w(max(values)) txt.nl() txt.print_w(min(values)) diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index c6e04753f..29cef0c40 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -25,8 +25,22 @@ SYSCALLS: 16 = sort_byte array 17 = sort_uword array 18 = sort_word array -19 = reverse_bytes array -20 = reverse_words array +19 = max_ubyte array +20 = max_byte array +21 = max_uword array +22 = max_word array +23 = min_ubyte array +24 = min_byte array +25 = min_uword array +26 = min_word array +27 = sum_byte array +28 = sum_word array +29 = any_byte array +30 = any_word array +31 = all_byte array +32 = all_word array +33 = reverse_bytes array +34 = reverse_words array */ enum class Syscall { @@ -49,8 +63,22 @@ enum class Syscall { SORT_BYTE, SORT_UWORD, SORT_WORD, - REVERSE_BYTES, // TODO not as syscall - REVERSE_WORDS // TODO not as syscall + MAX_UBYTE, + MAX_BYTE, + MAX_UWORD, + MAX_WORD, + MIN_UBYTE, + MIN_BYTE, + MIN_UWORD, + MIN_WORD, + SUM_BYTE, + SUM_WORD, + ANY_BYTE, + ANY_WORD, + ALL_BYTE, + ALL_WORD, + REVERSE_BYTES, + REVERSE_WORDS } object SysCalls { @@ -168,6 +196,112 @@ object SysCalls { vm.memory.setUW(address+index*2, value) } } + Syscall.MAX_UBYTE -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getUB(it) }.maxOf { it } + vm.registers.setUB(0, value) + } + Syscall.MAX_BYTE -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getSB(it) }.maxOf { it } + vm.registers.setSB(0, value) + } + Syscall.MAX_UWORD -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getUW(it) }.maxOf { it } + vm.registers.setUW(0, value) + } + Syscall.MAX_WORD -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getSW(it) }.maxOf { it } + vm.registers.setSW(0, value) + } + Syscall.MIN_UBYTE -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getUB(it) }.minOf { it } + vm.registers.setUB(0, value) + } + Syscall.MIN_BYTE -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getSB(it) }.minOf { it } + vm.registers.setSB(0, value) + } + Syscall.MIN_UWORD -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getUW(it) }.minOf { it } + vm.registers.setUW(0, value) + } + Syscall.MIN_WORD -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getSW(it) }.minOf { it } + vm.registers.setSW(0, value) + } + Syscall.SUM_BYTE -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getUB(it) }.sum() + vm.registers.setUB(0, value.toUByte()) + } + Syscall.SUM_WORD -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + val value = addresses.map { vm.memory.getUW(it) }.sum() + vm.registers.setUW(0, value.toUShort()) + } + Syscall.ANY_BYTE -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + if(addresses.any { vm.memory.getUB(it).toInt()!=0 }) + vm.registers.setUB(0, 1u) + else + vm.registers.setUB(0, 0u) + } + Syscall.ANY_WORD -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + if(addresses.any { vm.memory.getUW(it).toInt()!=0 }) + vm.registers.setUB(0, 1u) + else + vm.registers.setUB(0, 0u) + } + Syscall.ALL_BYTE -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + if(addresses.all { vm.memory.getUB(it).toInt()!=0 }) + vm.registers.setUB(0, 1u) + else + vm.registers.setUB(0, 0u) + } + Syscall.ALL_WORD -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).toInt() + val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) + if(addresses.all { vm.memory.getUW(it).toInt()!=0 }) + vm.registers.setUB(0, 1u) + else + vm.registers.setUB(0, 0u) + } else -> TODO("syscall ${call.name}") } }