diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index 03e3e3305..b9171db7c 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -10,151 +10,29 @@ import prog8.vm.VmDataType internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: ExpressionGen) { fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { - val code = VmCodeChunk() - when(call.name) { - "syscall" -> { - val vExpr = call.args.single() as PtNumber - code += VmCodeInstruction(Opcode.SYSCALL, value=vExpr.number.toInt()) - } - "syscall1" -> { - val callNr = (call.args[0] as PtNumber).number.toInt() - code += exprGen.translateExpression(call.args[1], 0) - code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) - } - "syscall2" -> { - code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1) - while(codeGen.vmRegisters.peekNext()<2) { - codeGen.vmRegisters.nextFree() - } - val callNr = (call.args[0] as PtNumber).number.toInt() - code += exprGen.translateExpression(call.args[1], 0) - code += exprGen.translateExpression(call.args[2], 1) - code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) - code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1) - } - "syscall3" -> { - code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1) - code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2) - while(codeGen.vmRegisters.peekNext()<3) { - codeGen.vmRegisters.nextFree() - } - val callNr = (call.args[0] as PtNumber).number.toInt() - code += exprGen.translateExpression(call.args[1], 0) - code += exprGen.translateExpression(call.args[2], 1) - code += exprGen.translateExpression(call.args[3], 2) - code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) - code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 2) - code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1) - } - "msb" -> { - code += exprGen.translateExpression(call.args.single(), resultRegister) - code += VmCodeInstruction(Opcode.SWAP, VmDataType.BYTE, reg1 = resultRegister) - // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. - } - "lsb" -> { - code += exprGen.translateExpression(call.args.single(), resultRegister) - // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. - } - "memory" -> { - val name = (call.args[0] as PtString).value - val size = (call.args[1] as PtNumber).number.toUInt() - val align = (call.args[2] as PtNumber).number.toUInt() - val existing = codeGen.allocations.getMemorySlab(name) - val address = if(existing==null) - codeGen.allocations.allocateMemorySlab(name, size, align) - else if(existing.second!=size || existing.third!=align) { - codeGen.errors.err("memory slab '$name' already exists with a different size or alignment", call.position) - return VmCodeChunk() - } - else - existing.first - code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, value=address.toInt()) - } - "rnd" -> { - code += VmCodeInstruction(Opcode.SYSCALL, value= Syscall.RND.ordinal) - if(resultRegister!=0) - code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) - } - "peek" -> { - // should just be a memory read - val addressReg = codeGen.vmRegisters.nextFree() - code += exprGen.translateExpression(call.args.single(), addressReg) - code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg) - } - "peekw" -> { - val addressReg = codeGen.vmRegisters.nextFree() - code += exprGen.translateExpression(call.args.single(), addressReg) - code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg) - } - "poke" -> { - // should just be a memory write - val addressReg = codeGen.vmRegisters.nextFree() - val valueReg = codeGen.vmRegisters.nextFree() - code += exprGen.translateExpression(call.args[0], addressReg) - code += exprGen.translateExpression(call.args[1], valueReg) - code += VmCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = addressReg, reg2=valueReg) - } - "pokew" -> { - val addressReg = codeGen.vmRegisters.nextFree() - val valueReg = codeGen.vmRegisters.nextFree() - code += exprGen.translateExpression(call.args[0], addressReg) - code += exprGen.translateExpression(call.args[1], valueReg) - code += VmCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = addressReg, reg2=valueReg) - } - "mkword" -> { - val msbReg = codeGen.vmRegisters.nextFree() - val lsbReg = codeGen.vmRegisters.nextFree() - code += exprGen.translateExpression(call.args[0], msbReg) - code += exprGen.translateExpression(call.args[1], lsbReg) - code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg) - } - "sin8u" -> { - code += exprGen.translateExpression(call.args[0], 0) - code += VmCodeInstruction(Opcode.SYSCALL, value=Syscall.SIN8U.ordinal) - if(resultRegister!=0) - code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) - } - "cos8u" -> { - code += exprGen.translateExpression(call.args[0], 0) - code += VmCodeInstruction(Opcode.SYSCALL, value=Syscall.COS8U.ordinal) - 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) - } - "rol" -> RolRor2(Opcode.ROXL, call, resultRegister, code) - "ror" -> RolRor2(Opcode.ROXR, call, resultRegister, code) - "rol2" -> RolRor2(Opcode.ROL, call, resultRegister, code) - "ror2" -> RolRor2(Opcode.ROR, call, resultRegister, code) + return when(call.name) { + "syscall" -> funcSyscall(call) + "syscall1" -> funcSyscall1(call) + "syscall2" -> funcSyscall2(call) + "syscall3" -> funcSyscall3(call) + "msb" -> funcMsb(call, resultRegister) + "lsb" -> funcLsb(call, resultRegister) + "memory" -> funcMemory(call, resultRegister) + "rnd" -> funcRnd(call, resultRegister) + "peek" -> funcPeek(call, resultRegister) + "peekw" -> funcPeekW(call, resultRegister) + "poke" -> funcPoke(call, resultRegister) + "pokew" -> funcPokeW(call, resultRegister) + "pokemon" -> VmCodeChunk() + "mkword" -> funcMkword(call, resultRegister) + "sin8u" -> funcSin8u(call, resultRegister) + "cos8u" -> funcCos8u(call, resultRegister) + "sort" -> funcSort(call) + "reverse" -> funcReverse(call) + "rol" -> funcRolRor2(Opcode.ROXL, call, resultRegister) + "ror" -> funcRolRor2(Opcode.ROXR, call, resultRegister) + "rol2" -> funcRolRor2(Opcode.ROL, call, resultRegister) + "ror2" -> funcRolRor2(Opcode.ROR, call, resultRegister) else -> { TODO("builtinfunc ${call.name}") // code += VmCodeInstruction(Opcode.NOP)) @@ -177,12 +55,202 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: // code += VmCodeInstruction(Opcode.NOP)) } } + } + + private fun funcReverse(call: PtBuiltinFunctionCall): VmCodeChunk { + 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") + } + 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=sortSyscall.ordinal) return code } - private fun RolRor2(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int, code: VmCodeChunk) { + private fun funcSort(call: PtBuiltinFunctionCall): VmCodeChunk { + 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") + } + 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=sortSyscall.ordinal) + return code + } + + private fun funcCos8u(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.SYSCALL, value=Syscall.COS8U.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) + return code + } + + private fun funcSin8u(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], 0) + code += VmCodeInstruction(Opcode.SYSCALL, value=Syscall.SIN8U.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) + return code + } + + private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val msbReg = codeGen.vmRegisters.nextFree() + val lsbReg = codeGen.vmRegisters.nextFree() + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], msbReg) + code += exprGen.translateExpression(call.args[1], lsbReg) + code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg) + return code + } + + private fun funcPokeW(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val addressReg = codeGen.vmRegisters.nextFree() + val valueReg = codeGen.vmRegisters.nextFree() + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], addressReg) + code += exprGen.translateExpression(call.args[1], valueReg) + code += VmCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = addressReg, reg2=valueReg) + return code + } + + private fun funcPoke(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + // should just be a memory write + val addressReg = codeGen.vmRegisters.nextFree() + val valueReg = codeGen.vmRegisters.nextFree() + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args[0], addressReg) + code += exprGen.translateExpression(call.args[1], valueReg) + code += VmCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = addressReg, reg2=valueReg) + return code + } + + private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val addressReg = codeGen.vmRegisters.nextFree() + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args.single(), addressReg) + code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg) + return code + } + + private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + // should just be a memory read + val code = VmCodeChunk() + val addressReg = codeGen.vmRegisters.nextFree() + code += exprGen.translateExpression(call.args.single(), addressReg) + code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg) + return code + } + + private fun funcRnd(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val code = VmCodeChunk() + code += VmCodeInstruction(Opcode.SYSCALL, value= Syscall.RND.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) + return code + } + + private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val name = (call.args[0] as PtString).value + val size = (call.args[1] as PtNumber).number.toUInt() + val align = (call.args[2] as PtNumber).number.toUInt() + val existing = codeGen.allocations.getMemorySlab(name) + val address = if(existing==null) + codeGen.allocations.allocateMemorySlab(name, size, align) + else if(existing.second!=size || existing.third!=align) { + codeGen.errors.err("memory slab '$name' already exists with a different size or alignment", call.position) + return VmCodeChunk() + } + else + existing.first + val code = VmCodeChunk() + code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, value=address.toInt()) + return code + } + + private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args.single(), resultRegister) + // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. + return code + } + + private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args.single(), resultRegister) + code += VmCodeInstruction(Opcode.SWAP, VmDataType.BYTE, reg1 = resultRegister) + // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. + return code + } + + private fun funcSyscall(call: PtBuiltinFunctionCall): VmCodeChunk { + val code = VmCodeChunk() + val vExpr = call.args.single() as PtNumber + code += VmCodeInstruction(Opcode.SYSCALL, value=vExpr.number.toInt()) + return code + } + + private fun funcSyscall1(call: PtBuiltinFunctionCall): VmCodeChunk { + val code = VmCodeChunk() + val callNr = (call.args[0] as PtNumber).number.toInt() + code += exprGen.translateExpression(call.args[1], 0) + code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) + return code + } + + private fun funcSyscall2(call: PtBuiltinFunctionCall): VmCodeChunk { + val code = VmCodeChunk() + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1) + while(codeGen.vmRegisters.peekNext()<2) { + codeGen.vmRegisters.nextFree() + } + val callNr = (call.args[0] as PtNumber).number.toInt() + code += exprGen.translateExpression(call.args[1], 0) + code += exprGen.translateExpression(call.args[2], 1) + code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1) + return code + } + + private fun funcSyscall3(call: PtBuiltinFunctionCall): VmCodeChunk { + val code = VmCodeChunk() + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2) + while(codeGen.vmRegisters.peekNext()<3) { + codeGen.vmRegisters.nextFree() + } + val callNr = (call.args[0] as PtNumber).number.toInt() + code += exprGen.translateExpression(call.args[1], 0) + code += exprGen.translateExpression(call.args[2], 1) + code += exprGen.translateExpression(call.args[3], 2) + code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 2) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1) + return code + } + + private fun funcRolRor2(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { // bit rotate left without carry, in-place val vmDt = codeGen.vmType(call.args[0].type) + val code = VmCodeChunk() code += exprGen.translateExpression(call.args[0], resultRegister) code += VmCodeInstruction(opcode, vmDt, reg1=resultRegister) val assignment = PtAssignment(call.position) @@ -191,6 +259,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: assignment.children.add(target) assignment.children.add(PtIdentifier(listOf(":vmreg-$resultRegister"), listOf(":vmreg-$resultRegister"), call.args[0].type, call.position)) code += codeGen.translateNode(assignment) + return code } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 785d646dd..02d8c6a53 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,7 @@ TODO For next release ^^^^^^^^^^^^^^^^ +- 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 diff --git a/examples/test.p8 b/examples/test.p8 index f467b2437..3a3e8604c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,16 +5,15 @@ ; NOTE: meant to test to virtual machine output target (use -target vitual) main { - %option align_word - %option force_output - sub start() { + pokemon(1,0) + ; a "pixelshader": void syscall1(8, 0) ; enable lo res creen ubyte shifter - shifter >>= 1 + ; pokemon(1,0) repeat { uword xx