diff --git a/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt b/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt index f988da600..28aca4118 100644 --- a/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt +++ b/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt @@ -44,5 +44,5 @@ class VirtualMachineDefinition: IMachineDefinition { } interface IVirtualMachineRunner { - fun runProgram(program: String, throttle: Boolean) + fun runProgram(source: String, throttle: Boolean) } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index ce196b856..1c473941e 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.AssemblyError import prog8.code.core.DataType import prog8.vm.Opcode import prog8.vm.Syscall @@ -11,6 +12,54 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { return when(call.name) { + "cmp" -> TODO() + "max" -> TODO() + "min" -> TODO() + "sum" -> TODO() + "abs" -> TODO() + "sgn" -> funcSgn(call, resultRegister) + "sin" -> TODO("floats not yet implemented") + "sin8" -> TODO() + "sin16" -> TODO() + "sin16u" -> TODO() + "sinr8" -> TODO() + "sinr8u" -> TODO() + "sinr16" -> TODO() + "sinr16u" -> TODO() + "cos" -> TODO("floats not yet implemented") + "cos8" -> TODO() + "cos16" -> TODO() + "cos16u" -> TODO() + "cosr8" -> TODO() + "cosr8u" -> TODO() + "cosr16" -> TODO() + "cosr16u" -> TODO() + "tan" -> TODO("floats not yet implemented") + "atan" -> TODO("floats not yet implemented") + "ln" -> TODO("floats not yet implemented") + "log2" -> TODO("floats not yet implemented") + "sqrt16" -> funcSqrt16(call, resultRegister) + "sqrt" -> TODO("floats not yet implemented") + "rad" -> TODO("floats not yet implemented") + "deg" -> TODO("floats not yet implemented") + "round" -> TODO("floats not yet implemented") + "floor" -> TODO("floats not yet implemented") + "ceil" -> TODO("floats not yet implemented") + "any" -> TODO() + "all" -> TODO() + "pop" -> funcPop(call) + "popw" -> funcPopw(call) + "push" -> funcPush(call) + "pushw" -> funcPushw(call) + "rsave", + "rsavex", + "rrestore", + "rrestorex" -> VmCodeChunk() // vm doesn't have registers to save/restore + "rnd" -> funcRnd(resultRegister) + "rndw" -> funcRndw(resultRegister) + "rndf" -> TODO("floats not yet implemented") + "callfar" -> throw AssemblyError("callfar() is for cx16 target only") + "callrom" -> throw AssemblyError("callrom() is for cx16 target only") "syscall" -> funcSyscall(call) "syscall1" -> funcSyscall1(call) "syscall2" -> funcSyscall2(call) @@ -18,7 +67,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: "msb" -> funcMsb(call, resultRegister) "lsb" -> funcLsb(call, resultRegister) "memory" -> funcMemory(call, resultRegister) - "rnd" -> funcRnd(resultRegister) "peek" -> funcPeek(call, resultRegister) "peekw" -> funcPeekW(call, resultRegister) "poke" -> funcPoke(call) @@ -34,30 +82,52 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: "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)) -// for (arg in call.args) { -// code += translateExpression(arg, resultRegister) -// code += when(arg.type) { -// in ByteDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=resultRegister)) -// in WordDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=resultRegister)) -// else -> throw AssemblyError("weird arg dt") -// } -// } -// code += VmCodeInstruction(Opcode.CALL), labelArg = listOf("_prog8_builtin", call.name)) -// for (arg in call.args) { -// code += when(arg.type) { -// in ByteDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=resultRegister)) -// in WordDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=resultRegister)) -// else -> throw AssemblyError("weird arg dt") -// } -// } -// code += VmCodeInstruction(Opcode.NOP)) - } + else -> TODO("builtinfunc ${call.name}") } } + private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args.single(), 0) + code += VmCodeInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=0) + return code + } + + private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args.single(), 0) + code += VmCodeInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=0) + return code + } + + private fun funcPop(call: PtBuiltinFunctionCall): VmCodeChunk { + val code = VmCodeChunk() + code += VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=0) + code += assignRegisterTo(call.args.single(), 0) + return code + } + + private fun funcPopw(call: PtBuiltinFunctionCall): VmCodeChunk { + val code = VmCodeChunk() + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=0) + code += assignRegisterTo(call.args.single(), 0) + return code + } + + private fun funcPush(call: PtBuiltinFunctionCall): VmCodeChunk { + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args.single(), 0) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=0) + return code + } + + private fun funcPushw(call: PtBuiltinFunctionCall): VmCodeChunk { + val code = VmCodeChunk() + code += exprGen.translateExpression(call.args.single(), 0) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=0) + return code + } + private fun funcSwap(call: PtBuiltinFunctionCall): VmCodeChunk { val left = call.args[0] val right = call.args[1] @@ -202,6 +272,14 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: return code } + private fun funcRndw(resultRegister: Int): VmCodeChunk { + val code = VmCodeChunk() + code += VmCodeInstruction(Opcode.SYSCALL, value= Syscall.RNDW.ordinal) + if(resultRegister!=0) + code += VmCodeInstruction(Opcode.LOADR, VmDataType.WORD, 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() diff --git a/examples/test.p8 b/examples/test.p8 index 099105acf..e1eb846b0 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,11 +7,14 @@ main { sub start() { - pokew($1000, $ea31) - poke($1001, $44) - txt.print_uwhex(peekw($1000), true) + word zz = 0 + txt.print_b(sgn(zz)) txt.nl() - txt.print_ubhex(peek($1001), true) + zz = -100 + txt.print_b(sgn(zz)) + txt.nl() + zz = 9999 + txt.print_b(sgn(zz)) txt.nl() ; uword other = $fe4a diff --git a/virtualmachine/src/prog8/vm/Instructions.kt b/virtualmachine/src/prog8/vm/Instructions.kt index b1af32a2f..30e173152 100644 --- a/virtualmachine/src/prog8/vm/Instructions.kt +++ b/virtualmachine/src/prog8/vm/Instructions.kt @@ -6,7 +6,7 @@ Virtual machine: 65536 virtual registers, 16 bits wide, can also be used as 8 bits. r0-r65535 65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits. -Value stack, max 128 entries. +Value stack, max 128 entries of 1 byte each. Status registers: Carry. @@ -107,6 +107,8 @@ sub reg1, reg2, reg3 - reg1 = reg2-reg3 (unsigned + signe mul reg1, reg2, reg3 - unsigned multiply reg1=reg2*reg3 note: byte*byte->byte, no type extension to word! div reg1, reg2, reg3 - unsigned division reg1=reg2/reg3 note: division by zero yields max signed int $ff/$ffff mod reg1, reg2, reg3 - remainder (modulo) of unsigned division reg1=reg2%reg3 note: division by zero yields max signed int $ff/$ffff +sqrt reg1, reg2 - reg1 is the square root of reg2 (for .w and .b both , the result is a byte) +sgn reg1, reg2 - reg1 is the sign of reg2 (0, 1 or -1) NOTE: because mul/div are constrained (truncated) to remain in 8 or 16 bits, there is NO NEED for separate signed/unsigned mul and div instructions. The result is identical. @@ -203,6 +205,8 @@ enum class Opcode { MUL, DIV, MOD, + SQRT, + SGN, EXT, EXTS, @@ -227,8 +231,6 @@ enum class Opcode { MSIG, SWAPREG, CONCAT, - COPY, - COPYZ, BREAKPOINT } @@ -372,6 +374,8 @@ val instructionFormats = mutableMapOf( Opcode.MUL to InstructionFormat(BW, true, true, true, false), Opcode.DIV to InstructionFormat(BW, true, true, true, false), Opcode.MOD to InstructionFormat(BW, true, true, true, false), + Opcode.SQRT to InstructionFormat(BW, true, true, false, false), + Opcode.SGN to InstructionFormat(BW, true, true, false, false), Opcode.EXT to InstructionFormat(BW, true, false, false, false), Opcode.EXTS to InstructionFormat(BW, true, false, false, false), @@ -389,8 +393,6 @@ val instructionFormats = mutableMapOf( Opcode.ROL to InstructionFormat(BW, true, false, false, false), Opcode.ROXL to InstructionFormat(BW, true, false, false, false), - Opcode.COPY to InstructionFormat(NN, true, true, false, true ), - Opcode.COPYZ to InstructionFormat(NN, true, true, false, false), Opcode.MSIG to InstructionFormat(BW, true, true, false, false), Opcode.PUSH to InstructionFormat(BW, true, false, false, false), Opcode.POP to InstructionFormat(BW, true, false, false, false), diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index 816512568..63c81d6ee 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -18,16 +18,17 @@ SYSCALLS: 9 = gfx_clear ; clear graphics window with shade in r0.b 10 = gfx_plot ; plot pixel in graphics window, r0.w/r1.w contain X and Y coordinates, r2.b contains brightness 11 = rnd ; random BYTE -12 = wait ; wait certain amount of jiffies (1/60 sec) -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 +12 = rndw ; random WORD +13 = wait ; wait certain amount of jiffies (1/60 sec) +14 = waitvsync ; wait on vsync +15 = sin8u +16 = cos8u +17 = sort_ubyte array +18 = sort_byte array +19 = sort_uword array +20 = sort_word array +21 = reverse_bytes array +22 = reverse_words array */ enum class Syscall { @@ -43,6 +44,7 @@ enum class Syscall { GFX_CLEAR, GFX_PLOT, RND, + RNDW, WAIT, WAITVSYNC, SIN8U, @@ -100,7 +102,10 @@ object SysCalls { Syscall.GFX_CLEAR -> vm.gfx_clear() Syscall.GFX_PLOT -> vm.gfx_plot() Syscall.RND -> { - vm.registers.setUB(0, (Random.nextInt() ushr 3).toUByte()) + vm.registers.setUB(0, Random.nextInt().toUByte()) + } + Syscall.RNDW -> { + vm.registers.setUW(0, Random.nextInt().toUShort()) } Syscall.WAIT -> { val millis = vm.registers.getUW(0).toLong() * 1000/60 diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index e48362d6d..b3ba4e910 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -4,6 +4,8 @@ import prog8.code.target.virtual.IVirtualMachineRunner import java.awt.Toolkit import java.util.* import kotlin.math.roundToInt +import kotlin.math.sign +import kotlin.math.sqrt class ProgramExitException(val status: Int): Exception() @@ -17,7 +19,7 @@ class VirtualMachine(val memory: Memory, program: List) { val registers = Registers() val program: Array = program.toTypedArray() val callStack = Stack() - val valueStack = Stack() // max 128 entries + val valueStack = Stack() // max 128 entries var pc = 0 var stepCount = 0 var statusCarry = false @@ -135,6 +137,8 @@ class VirtualMachine(val memory: Memory, program: List) { Opcode.MUL -> InsMUL(ins) Opcode.DIV -> InsDIV(ins) Opcode.MOD -> InsMOD(ins) + Opcode.SQRT -> InsSQRT(ins) + Opcode.SGN -> InsSGN(ins) Opcode.EXT -> InsEXT(ins) Opcode.EXTS -> InsEXTS(ins) Opcode.AND -> InsAND(ins) @@ -154,8 +158,6 @@ class VirtualMachine(val memory: Memory, program: List) { Opcode.CONCAT -> InsCONCAT(ins) Opcode.PUSH -> InsPUSH(ins) Opcode.POP -> InsPOP(ins) - Opcode.COPY -> InsCOPY(ins) - Opcode.COPYZ -> InsCOPYZ(ins) Opcode.BREAKPOINT -> InsBREAKPOINT() else -> throw IllegalArgumentException("invalid opcode ${ins.opcode}") } @@ -172,17 +174,32 @@ class VirtualMachine(val memory: Memory, program: List) { if(valueStack.size>=128) throw StackOverflowError("valuestack limit 128 exceeded") - val value = when(i.type!!) { - VmDataType.BYTE -> registers.getUB(i.reg1!!).toInt() - VmDataType.WORD -> registers.getUW(i.reg1!!).toInt() + when(i.type!!) { + VmDataType.BYTE -> { + val value = registers.getUB(i.reg1!!) + valueStack.push(value) + } + VmDataType.WORD -> { + val value = registers.getUW(i.reg1!!) + valueStack.push((value and 255u).toUByte()) + valueStack.push((value.toInt() ushr 8).toUByte()) + } } - valueStack.push(value) pc++ } private fun InsPOP(i: Instruction) { - val value = valueStack.pop() - setResultReg(i.reg1!!, value, i.type!!) + val value = when(i.type!!) { + VmDataType.BYTE -> { + valueStack.pop().toInt() + } + VmDataType.WORD -> { + val msb = valueStack.pop() + val lsb = valueStack.pop() + (msb.toInt() shl 8) + lsb.toInt() + } + } + setResultReg(i.reg1!!, value, i.type) pc++ } @@ -597,6 +614,22 @@ class VirtualMachine(val memory: Memory, program: List) { pc++ } + private fun InsSQRT(i: Instruction) { + when(i.type!!) { + VmDataType.BYTE -> registers.setUB(i.reg1!!, sqrt(registers.getUB(i.reg2!!).toDouble()).toInt().toUByte()) + VmDataType.WORD -> registers.setUB(i.reg1!!, sqrt(registers.getUW(i.reg2!!).toDouble()).toInt().toUByte()) + } + pc++ + } + + private fun InsSGN(i: Instruction) { + when(i.type!!) { + VmDataType.BYTE -> registers.setSB(i.reg1!!, registers.getSB(i.reg2!!).toInt().sign.toByte()) + VmDataType.WORD -> registers.setSW(i.reg1!!, registers.getSW(i.reg2!!).toInt().sign.toShort()) + } + pc++ + } + private fun arithByte(operator: String, reg1: Int, reg2: Int, reg3: Int?, value: UByte?) { val left = registers.getUB(reg2) val right = value ?: registers.getUB(reg3!!) @@ -851,35 +884,6 @@ class VirtualMachine(val memory: Memory, program: List) { pc++ } - private fun InsCOPY(i: Instruction) = doCopy(i.reg1!!, i.reg2!!, i.reg3!!, false) - - private fun InsCOPYZ(i: Instruction) = doCopy(i.reg1!!, i.reg2!!, null, true) - - private fun doCopy(reg1: Int, reg2: Int, length: Int?, untilzero: Boolean) { - var from = registers.getUW(reg1).toInt() - var to = registers.getUW(reg2).toInt() - if(untilzero) { - while(true) { - val char = memory.getUB(from) - memory.setUB(to, char) - if(char.toInt()==0) - break - from++ - to++ - } - } else { - var len = length!! - while(len>0) { - val char = memory.getUB(from) - memory.setUB(to, char) - from++ - to++ - len-- - } - } - pc++ - } - private fun getBranchOperands(i: Instruction): Pair { return when(i.type) { VmDataType.BYTE -> Pair(registers.getSB(i.reg1!!).toInt(), registers.getSB(i.reg2!!).toInt()) @@ -957,7 +961,7 @@ class VirtualMachine(val memory: Memory, program: List) { } } - +// probably called via reflection class VmRunner(): IVirtualMachineRunner { override fun runProgram(source: String, throttle: Boolean) { val (memsrc, programsrc) = source.split("------PROGRAM------".toRegex(), 2)