mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
vm: add many builtin functions
This commit is contained in:
parent
a2c7273801
commit
51bf33040a
@ -44,5 +44,5 @@ class VirtualMachineDefinition: IMachineDefinition {
|
||||
}
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(program: String, throttle: Boolean)
|
||||
fun runProgram(source: String, throttle: Boolean)
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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<Instruction>) {
|
||||
val registers = Registers()
|
||||
val program: Array<Instruction> = program.toTypedArray()
|
||||
val callStack = Stack<Int>()
|
||||
val valueStack = Stack<Int>() // max 128 entries
|
||||
val valueStack = Stack<UByte>() // max 128 entries
|
||||
var pc = 0
|
||||
var stepCount = 0
|
||||
var statusCarry = false
|
||||
@ -135,6 +137,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
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<Instruction>) {
|
||||
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<Instruction>) {
|
||||
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<Instruction>) {
|
||||
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<Instruction>) {
|
||||
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<Int, Int> {
|
||||
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<Instruction>) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// probably called via reflection
|
||||
class VmRunner(): IVirtualMachineRunner {
|
||||
override fun runProgram(source: String, throttle: Boolean) {
|
||||
val (memsrc, programsrc) = source.split("------PROGRAM------".toRegex(), 2)
|
||||
|
Loading…
Reference in New Issue
Block a user