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}")
}
}