From 8e4c0f7c220b306c7e2b9409199f87b3e2dc3dff Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 5 Apr 2022 17:48:49 +0200 Subject: [PATCH] vm: add sorting and reverse functions, fix value arg out of range errors --- .../prog8/codegen/virtual/AssemblyProgram.kt | 3 +- .../prog8/codegen/virtual/BuiltinFuncGen.kt | 35 +++++++++ docs/source/todo.rst | 1 + examples/sorting.p8 | 1 - virtualmachine/src/prog8/vm/Assembler.kt | 5 +- virtualmachine/src/prog8/vm/Instructions.kt | 10 +++ virtualmachine/src/prog8/vm/SysCalls.kt | 74 ++++++++++++++++++- 7 files changed, 125 insertions(+), 4 deletions(-) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt index c082a7ec3..b32d7e47c 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt @@ -4,6 +4,7 @@ import prog8.code.core.CompilationOptions import prog8.code.core.IAssemblyProgram import prog8.vm.Instruction import prog8.vm.Opcode +import prog8.vm.OpcodesWithAddress import prog8.vm.VmDataType import java.io.BufferedWriter import kotlin.io.path.bufferedWriter @@ -66,7 +67,7 @@ internal class VmCodeInstruction( val ins = Instruction(opcode, type, reg1, reg2, reg3, value, symbol) init { - if(value!=null) { + if(value!=null && opcode !in OpcodesWithAddress) { when (type) { VmDataType.BYTE -> { if (value < -128 || value > 255) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index e622ef3ce..19546d5db 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -1,8 +1,12 @@ package prog8.codegen.virtual +import prog8.code.StArray +import prog8.code.StStaticVariable import prog8.code.ast.PtBuiltinFunctionCall +import prog8.code.ast.PtIdentifier import prog8.code.ast.PtNumber import prog8.code.ast.PtString +import prog8.code.core.DataType import prog8.code.core.WordDatatypes import prog8.vm.Opcode import prog8.vm.Syscall @@ -127,6 +131,37 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: if(resultRegister!=0) code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) } + "sort" -> { + val arrayName = call.args[0] as PtIdentifier + val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable + val sortSyscall = + when(array.dt) { + DataType.ARRAY_UB -> Syscall.SORT_UBYTE + DataType.ARRAY_B -> Syscall.SORT_BYTE + DataType.ARRAY_UW -> Syscall.SORT_UWORD + DataType.ARRAY_W -> Syscall.SORT_WORD + DataType.FLOAT -> TODO("float sort") + DataType.STR -> Syscall.SORT_UBYTE + else -> throw IllegalArgumentException("weird type to sort") + } + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) + code += VmCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal) + } + "reverse" -> { + val arrayName = call.args[0] as PtIdentifier + val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable + val sortSyscall = + 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") + else -> throw IllegalArgumentException("weird type to reverse") + } + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) + code += VmCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal) + } else -> { TODO("builtinfunc ${call.name}") // code += VmCodeInstruction(Opcode.NOP)) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 017e3d5c0..6a6372189 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,7 @@ TODO For next release ^^^^^^^^^^^^^^^^ +- add -vm option to load an existing p8virt file directly in the virtual machine - pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls. - writeAssembly(): 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 diff --git a/examples/sorting.p8 b/examples/sorting.p8 index 163f23a4a..f584dec42 100644 --- a/examples/sorting.p8 +++ b/examples/sorting.p8 @@ -1,5 +1,4 @@ %import textio -%import test_stack %zeropage basicsafe ; Note: this program is compatible with C64 and CX16. diff --git a/virtualmachine/src/prog8/vm/Assembler.kt b/virtualmachine/src/prog8/vm/Assembler.kt index f83034720..d38bfa87d 100644 --- a/virtualmachine/src/prog8/vm/Assembler.kt +++ b/virtualmachine/src/prog8/vm/Assembler.kt @@ -132,7 +132,7 @@ class Assembler { throw IllegalArgumentException("invalid reg3 for $line") if(!format.value && value!=null) throw IllegalArgumentException("invalid value for $line") - if(value!=null) { + if(value!=null && opcode !in OpcodesWithAddress) { when (type) { VmDataType.BYTE -> { if (value < -128 || value > 255) @@ -161,6 +161,9 @@ class Assembler { } private fun parseValue(value: String, pc: Int): Int { + if(value.startsWith("-")) { + return -parseValue(value.substring(1), pc) + } if(value.startsWith('$')) return value.substring(1).toInt(16) if(value.startsWith('%')) diff --git a/virtualmachine/src/prog8/vm/Instructions.kt b/virtualmachine/src/prog8/vm/Instructions.kt index 17ce86025..f05ea3fb0 100644 --- a/virtualmachine/src/prog8/vm/Instructions.kt +++ b/virtualmachine/src/prog8/vm/Instructions.kt @@ -214,6 +214,16 @@ enum class Opcode { BREAKPOINT } +val OpcodesWithAddress = setOf( + Opcode.LOADM, + Opcode.LOADX, + Opcode.STOREM, + Opcode.STOREX, + Opcode.STOREZ, + Opcode.STOREZX +) + + enum class VmDataType { BYTE, WORD diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index e0bb8cd2a..0d250098b 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -22,6 +22,12 @@ SYSCALLS: 13 = waitvsync ; wait on vsync 14 = sin8u 15 = cos8u +16 = sort_ubyte array +17 = sort_byte array +18 = sort_uword array +19 = sort_word array +20 = reverse_bytes array +21 = reverse_words array */ enum class Syscall { @@ -40,7 +46,13 @@ enum class Syscall { WAIT, WAITVSYNC, SIN8U, - COS8U + COS8U, + SORT_UBYTE, + SORT_BYTE, + SORT_UWORD, + SORT_WORD, + REVERSE_BYTES, + REVERSE_WORDS } object SysCalls { @@ -107,6 +119,66 @@ object SysCalls { val answer = truncate(128.0 + 127.5 * cos(rad)) vm.registers.setUB(0, answer.toUInt().toUByte()) } + Syscall.SORT_UBYTE -> { + val address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).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 address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).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 address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).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 address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).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 address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).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 address = vm.registers.getUW(0).toInt() + val length = vm.registers.getUB(1).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) + } + } else -> TODO("syscall ${call.name}") } }