diff --git a/codeAst/src/prog8/code/ast/AstExpressions.kt b/codeAst/src/prog8/code/ast/AstExpressions.kt index 8f7d5c6d2..f4f0b9e68 100644 --- a/codeAst/src/prog8/code/ast/AstExpressions.kt +++ b/codeAst/src/prog8/code/ast/AstExpressions.kt @@ -107,6 +107,9 @@ class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpressi require(type!=DataType.UNDEFINED) } + val segments: List + get() = children.map { it as PtExpression } + override fun printProperties() {} } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index 4780f5665..789141379 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -98,6 +98,18 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: 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) + } else -> { TODO("builtinfunc ${call.name}") // code += VmCodeInstruction(Opcode.NOP)) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt index 25a124869..7066d5b44 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt @@ -16,22 +16,30 @@ internal class ExpressionGen(private val codeGen: CodeGen) { require(codeGen.vmRegisters.peekNext() > resultRegister) val code = VmCodeChunk() - val vmDt = codeGen.vmType(expr.type) when (expr) { is PtNumber -> { + val vmDt = codeGen.vmType(expr.type) code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt()) } is PtIdentifier -> { - val mem = codeGen.allocations.get(expr.targetName) - code += if(expr.type in PassByValueDatatypes) { - VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem) + val vmDt = codeGen.vmType(expr.type) + if(expr.targetName[0].startsWith(":vmreg-")) { + // special direct reference to a register in the VM + val reg = expr.targetName[0].substring(7).toInt() + code += VmCodeInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=reg) } else { - // for strings and arrays etc., load the *address* of the value instead - VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem) + val mem = codeGen.allocations.get(expr.targetName) + code += if (expr.type in PassByValueDatatypes) { + VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, value = mem) + } else { + // for strings and arrays etc., load the *address* of the value instead + VmCodeInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, value = mem) + } } } is PtAddressOf -> { + val vmDt = codeGen.vmType(expr.type) val mem = codeGen.allocations.get(expr.identifier.targetName) code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem) } @@ -57,7 +65,41 @@ internal class ExpressionGen(private val codeGen: CodeGen) { } internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk { - TODO("Not yet implemented: pipe expression") + val segments = pipe.segments + var valueDt = segments[0].type + var valueReg = if(pipe.void) codeGen.vmRegisters.nextFree() else resultRegister + + fun addImplicitArgToSegment(segment: PtExpression, sourceReg: Int, sourceDt: DataType): PtExpression { + return when (segment) { + is PtFunctionCall -> { + val segWithArg = PtFunctionCall(segment.functionName, segment.void, segment.type, segment.position) + segWithArg.children.add(0, PtIdentifier(listOf(":vmreg-$sourceReg"), listOf(":vmreg-$sourceReg"), sourceDt, segment.position)) + segWithArg + } + is PtBuiltinFunctionCall -> { + val segWithArg = PtBuiltinFunctionCall(segment.name, segment.void, segment.type, segment.position) + segWithArg.children.add(0, PtIdentifier(listOf(":vmreg-$sourceReg"), listOf(":vmreg-$sourceReg"), sourceDt, segment.position)) + segWithArg + } + else -> throw AssemblyError("weird segment type") + } + } + + val code = VmCodeChunk() + code += translateExpression(segments[0], valueReg) + for (segment in segments.subList(1, segments.size-1)) { + val sourceReg = valueReg + val sourceDt = valueDt + if(segment.type!=valueDt) { + valueDt = segment.type + valueReg = codeGen.vmRegisters.nextFree() + } + val segmentWithImplicitArgument = addImplicitArgToSegment(segment, sourceReg, sourceDt) + code += translateExpression(segmentWithImplicitArgument, valueReg) + } + val segWithArg = addImplicitArgToSegment(segments.last(), valueReg, valueDt) + code += translateExpression(segWithArg, resultRegister) + return code } private fun translate(check: PtContainmentCheck, resultRegister: Int): VmCodeChunk { diff --git a/compiler/src/prog8/compiler/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/IntermediateAstMaker.kt index 9708e3abc..f859ca101 100644 --- a/compiler/src/prog8/compiler/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/IntermediateAstMaker.kt @@ -211,8 +211,14 @@ class IntermediateAstMaker(val program: Program) { private fun transform(srcCall: FunctionCallExpression): PtFunctionCall { val (target, _) = targetOf(srcCall.target) - val type = srcCall.inferType(program).getOrElse { throw FatalAstException("unknown dt") } - val call = PtFunctionCall(target, false, type, srcCall.position) + val type = srcCall.inferType(program).getOrElse { + if((srcCall.parent as? Pipe)?.segments?.last() === srcCall) + // for a pipe, the last segment is allowed to be a call to a function not returning anything. + DataType.UNDEFINED + else + throw FatalAstException("unknown dt $srcCall") + } + val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position) for (arg in srcCall.args) call.add(transformExpression(arg)) return call @@ -291,8 +297,7 @@ class IntermediateAstMaker(val program: Program) { PtLabel(label.name, label.position) private fun transform(srcPipe: Pipe): PtPipe { - val type = srcPipe.segments.last().inferType(program).getOrElse { throw FatalAstException("unknown dt") } - val pipe = PtPipe(type, true, srcPipe.position) + val pipe = PtPipe(DataType.UNDEFINED, true, srcPipe.position) pipe.add(transformExpression(srcPipe.source)) for (segment in srcPipe.segments) pipe.add(transformExpression(segment)) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 6b69b9e26..849200dd1 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- vm codegen: Pipe expression - vm: support no globals re-init option +- vm: make registers typed? so that it's immediately obvious what type they represent. Much like regular variables in memory. - vm codegen/assembler: variable memory locations should also be referenced by the variable name instead of just the address, to make the output more human-readable - vm: how to remove all unused subroutines? (in the assembly codegen, we let 64tass solve this for us) - vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too diff --git a/examples/test.p8 b/examples/test.p8 index 98ea2a2d5..dfcc5bca3 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,32 +5,30 @@ main { - sub calculate(ubyte value) -> uword { - when value { - 1 -> return "one" - 2 -> return "two" - 3 -> return "three" - 4,5,6 -> return "four to six" - else -> return "other" - } + sub func1(ubyte arg1) -> uword { + return arg1 * 3 + } + + sub func2(uword arg1) -> uword { + return arg1+1000 + } + + sub func3(uword arg1) { + txt.print_uw(arg1+2000) + txt.nl() } sub start() { - txt.print(calculate(0)) + ubyte source = 99 + + uword result = func2(func1(cos8u(sin8u(source)))) + txt.print_uw(result) ; 1043 txt.nl() - txt.print(calculate(1)) - txt.nl() - txt.print(calculate(2)) - txt.nl() - txt.print(calculate(3)) - txt.nl() - txt.print(calculate(4)) - txt.nl() - txt.print(calculate(5)) - txt.nl() - txt.print(calculate(50)) + result = source |> sin8u() |> cos8u() |> func1() |> func2() + txt.print_uw(result) ; 1043 txt.nl() + source |> sin8u() |> cos8u() |> func1() |> func3() ; 2043 ; a "pixelshader": ; syscall1(8, 0) ; enable lo res creen diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index 97dcfcb58..e0bb8cd2a 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -1,6 +1,6 @@ package prog8.vm -import kotlin.math.min +import kotlin.math.* import kotlin.random.Random /* @@ -20,6 +20,8 @@ SYSCALLS: 11 = rnd ; random BYTE 12 = wait ; wait certain amount of jiffies (1/60 sec) 13 = waitvsync ; wait on vsync +14 = sin8u +15 = cos8u */ enum class Syscall { @@ -37,7 +39,8 @@ enum class Syscall { RND, WAIT, WAITVSYNC, - TMP_PRINT_UW + SIN8U, + COS8U } object SysCalls { @@ -92,8 +95,17 @@ object SysCalls { Thread.sleep(millis) } Syscall.WAITVSYNC -> vm.waitvsync() - Syscall.TMP_PRINT_UW -> { - println("VM: UW=${vm.registers.getUW(0)}") + Syscall.SIN8U -> { + val arg = vm.registers.getUB(0).toDouble() + val rad = arg /256.0 * 2.0 * PI + val answer = truncate(128.0 + 127.5 * sin(rad)) + vm.registers.setUB(0, answer.toUInt().toUByte()) + } + Syscall.COS8U -> { + val arg = vm.registers.getUB(0).toDouble() + val rad = arg /256.0 * 2.0 * PI + val answer = truncate(128.0 + 127.5 * cos(rad)) + vm.registers.setUB(0, answer.toUInt().toUByte()) } else -> TODO("syscall ${call.name}") }