working on vm

This commit is contained in:
Irmen de Jong 2022-03-27 14:23:01 +02:00
parent ed30108961
commit e41d6787bb
7 changed files with 267 additions and 87 deletions

View File

@ -161,6 +161,12 @@ class StStaticVariable(name: String,
require(arraysize == initialArrayValue.size)
if(arraysize!=null || initialArrayValue!=null)
require(initialStringValue==null && initialNumericValue==null)
if(initialNumericValue!=null)
require(dt in NumericDatatypes)
if(initialArrayValue!=null || arraysize!=null)
require(dt in ArrayDatatypes)
if(initialStringValue!=null)
require(dt == DataType.STR)
}
override fun printProperties() {

View File

@ -58,8 +58,8 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
val element: PtExpression
get() = children[0] as PtExpression
val iterable: PtExpression
get() = children[0] as PtExpression
val iterable: PtIdentifier
get() = children[0] as PtIdentifier
}

View File

@ -1,10 +1,12 @@
package prog8.codegen.virtual
import prog8.code.StStaticVariable
import prog8.code.StSub
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.PassByValueDatatypes
import prog8.code.core.SignedDatatypes
import prog8.vm.Instruction
import prog8.vm.Opcode
import prog8.vm.VmDataType
@ -54,7 +56,7 @@ internal class ExpressionGen(val codeGen: CodeGen) {
is PtBinaryExpression -> code += translate(expr, resultRegister, regUsage)
is PtBuiltinFunctionCall -> code += translate(expr, resultRegister, regUsage)
is PtFunctionCall -> code += translate(expr, resultRegister, regUsage)
is PtContainmentCheck -> TODO()
is PtContainmentCheck -> code += translate(expr, resultRegister, regUsage)
is PtPipe -> TODO()
is PtRange,
is PtArrayLiteral,
@ -64,6 +66,21 @@ internal class ExpressionGen(val codeGen: CodeGen) {
return code
}
private fun translate(check: PtContainmentCheck, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
val iterableIdent = check.iterable
val iterable = codeGen.symbolTable.flat.getValue(iterableIdent.targetName) as StStaticVariable
when(iterable.dt) {
DataType.STR -> println("CONTAINMENT CHECK ${check.element} in string $iterable ${iterable.initialStringValue}")
DataType.ARRAY_UB -> println("CONTAINMENT CHECK ${check.element} in UB-array $iterable ${iterable.initialArrayValue}")
DataType.ARRAY_B -> println("CONTAINMENT CHECK ${check.element} in B-array $iterable ${iterable.initialArrayValue}")
DataType.ARRAY_UW -> println("CONTAINMENT CHECK ${check.element} in UW-array $iterable ${iterable.initialArrayValue}")
DataType.ARRAY_W -> println("CONTAINMENT CHECK ${check.element} in W-array $iterable ${iterable.initialArrayValue}")
DataType.ARRAY_F -> TODO("containment check in float-array")
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${iterableIdent.targetName}")
}
return VmCodeChunk()
}
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
val vmDt = codeGen.vmType(arrayIx.type)
@ -194,9 +211,9 @@ internal class ExpressionGen(val codeGen: CodeGen) {
val code = VmCodeChunk()
val leftResultReg = regUsage.nextFree()
val rightResultReg = regUsage.nextFree()
// TODO: optimized codegen when left or right operand is known 0 or 1 or whatever.
val leftCode = translateExpression(binExpr.left, leftResultReg, regUsage)
val rightCode = translateExpression(binExpr.right, rightResultReg, regUsage)
// TODO: optimized codegen when left or right operand is known 0 or 1 or whatever.
code += leftCode
code += rightCode
val vmDt = codeGen.vmType(binExpr.type)
@ -231,7 +248,28 @@ internal class ExpressionGen(val codeGen: CodeGen) {
">>" -> {
code += VmCodeInstruction(Instruction(Opcode.LSR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
// TODO the other operators: "==", "!=", "<", ">", "<=", ">="
"==" -> {
code += VmCodeInstruction(Instruction(Opcode.SEQ, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"!=" -> {
code += VmCodeInstruction(Instruction(Opcode.SNE, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"<" -> {
val ins = if(binExpr.type in SignedDatatypes) Opcode.SLTS else Opcode.SLT
code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
">" -> {
val ins = if(binExpr.type in SignedDatatypes) Opcode.SGTS else Opcode.SGT
code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"<=" -> {
val ins = if(binExpr.type in SignedDatatypes) Opcode.SLES else Opcode.SLE
code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
">=" -> {
val ins = if(binExpr.type in SignedDatatypes) Opcode.SGES else Opcode.SGE
code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
else -> TODO("operator ${binExpr.operator}")
}
return code

View File

@ -6,6 +6,8 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.*
import prog8.code.core.ArrayDatatypes
import prog8.code.core.ElementToArrayTypes
import prog8.code.core.Position
import java.util.*
@ -59,6 +61,8 @@ internal class SymbolTableMaker: IAstVisitor {
val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
val initialArrayLit = decl.value as? ArrayLiteral
val initialArray = makeInitialArray(initialArrayLit)
if(decl.isArray && decl.datatype !in ArrayDatatypes)
throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
StStaticVariable(decl.name, decl.datatype, initialNumeric, initialString, initialArray, decl.arraysize?.constIndex(), decl.zeropage, decl.position)
}
VarDeclType.CONST -> StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, decl.position)

View File

@ -1,4 +1,3 @@
%import floats
%import textio
; NOTE: meant to test to virtual machine output target (use -target vitual)
@ -7,34 +6,43 @@ main {
sub start() {
txt.clear_screen()
txt.print("Welcome to a prog8 pixel shader :-)\n")
float fl = 9.9
fl = floats.pow(fl, 3.0)
txt.print("fl=")
floats.print_f(fl)
ubyte bb = 4
ubyte[] array = [1,2,3,4,5,6]
str tekst = "test"
uword ww = 19
bb = bb in "teststring"
bb++
bb = bb in [1,2,3,4,5,6]
bb++
bb = bb in array
bb++
bb = bb in tekst
txt.print("bb=")
txt.print_ub(bb)
txt.nl()
sys.exit(99)
; syscall1(8, 0) ; enable lo res creen
; ubyte shifter
;
; shifter >>= 1
;
; repeat {
; uword xx
; uword yy = 0
; repeat 240 {
; xx = 0
; repeat 320 {
; syscall3(10, xx, yy, xx*yy + shifter) ; plot pixel
; xx++
; }
; yy++
; }
; shifter+=4
;
; txt.print_ub(shifter)
; txt.nl()
; }
syscall1(8, 0) ; enable lo res creen
ubyte shifter
shifter >>= 1
repeat {
uword xx
uword yy = 0
repeat 240 {
xx = 0
repeat 320 {
syscall3(10, xx, yy, xx*yy + shifter) ; plot pixel
xx++
}
yy++
}
shifter+=4
txt.print_ub(shifter)
txt.nl()
}
}
}

View File

@ -74,6 +74,16 @@ bgt reg1, reg2, value - jump to location in program given by val
bgts reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (signed)
bge reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (unsigned)
bges reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (signed)
seq reg1, reg2, reg3 - set reg=1 if reg2 == reg3, otherwise set reg1=0
sne reg1, reg2, reg3 - set reg=1 if reg2 != reg3, otherwise set reg1=0
slt reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (unsigned), otherwise set reg1=0
slts reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (signed), otherwise set reg1=0
sle reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (unsigned), otherwise set reg1=0
sles reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (signed), otherwise set reg1=0
sgt reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (unsigned), otherwise set reg1=0
sgts reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (signed), otherwise set reg1=0
sge reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (unsigned), otherwise set reg1=0
sges reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (signed), otherwise set reg1=0
TODO: support for the prog8 special branching instructions if_XX (bcc, bcs etc.)
but we don't have any 'processor flags' whatsoever in the vm so it's a bit weird
@ -159,6 +169,16 @@ enum class Opcode {
BLES,
BGE,
BGES,
SEQ,
SNE,
SLT,
SLTS,
SGT,
SGTS,
SLE,
SLES,
SGE,
SGES,
INC,
DEC,
@ -271,6 +291,16 @@ val instructionFormats = mutableMapOf(
Opcode.BLES to InstructionFormat(BW, true, true, false, true ),
Opcode.BGE to InstructionFormat(BW, true, true, false, true ),
Opcode.BGES to InstructionFormat(BW, true, true, false, true ),
Opcode.SEQ to InstructionFormat(BW, true, true, true, false),
Opcode.SNE to InstructionFormat(BW, true, true, true, false),
Opcode.SLT to InstructionFormat(BW, true, true, true, false),
Opcode.SLTS to InstructionFormat(BW, true, true, true, false),
Opcode.SGT to InstructionFormat(BW, true, true, true, false),
Opcode.SGTS to InstructionFormat(BW, true, true, true, false),
Opcode.SLE to InstructionFormat(BW, true, true, true, false),
Opcode.SLES to InstructionFormat(BW, true, true, true, false),
Opcode.SGE to InstructionFormat(BW, true, true, true, false),
Opcode.SGES to InstructionFormat(BW, true, true, true, false),
Opcode.INC to InstructionFormat(BW, true, false, false, false),
Opcode.DEC to InstructionFormat(BW, true, false, false, false),

View File

@ -108,6 +108,17 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.BLES -> InsBLES(ins)
Opcode.BGE -> InsBGEU(ins)
Opcode.BGES -> InsBGES(ins)
Opcode.SEQ -> InsSEQ(ins)
Opcode.SNE -> InsSNE(ins)
Opcode.SLT -> InsSLT(ins)
Opcode.SLTS -> InsSLTS(ins)
Opcode.SGT -> InsSGT(ins)
Opcode.SGTS -> InsSGTS(ins)
Opcode.SLE -> InsSLE(ins)
Opcode.SLES -> InsSLES(ins)
Opcode.SGE -> InsSGE(ins)
Opcode.SGES -> InsSGES(ins)
Opcode.INC -> InsINC(ins)
Opcode.DEC -> InsDEC(ins)
Opcode.NEG -> InsNEG(ins)
@ -135,37 +146,33 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
}
private fun InsPUSH(ins: Instruction) {
private inline fun setResultReg(reg: Int, value: Int, type: VmDataType) {
when(type) {
VmDataType.BYTE -> registers.setB(reg, value.toUByte())
VmDataType.WORD -> registers.setW(reg, value.toUShort())
}
}
private fun InsPUSH(i: Instruction) {
if(valueStack.size>=128)
throw StackOverflowError("valuestack limit 128 exceeded")
val value = when(ins.type!!) {
VmDataType.BYTE -> {
registers.getB(ins.reg1!!).toInt()
}
VmDataType.WORD -> {
registers.getW(ins.reg1!!).toInt()
}
val value = when(i.type!!) {
VmDataType.BYTE -> registers.getB(i.reg1!!).toInt()
VmDataType.WORD -> registers.getW(i.reg1!!).toInt()
}
valueStack.push(value)
pc++
}
private fun InsPOP(ins: Instruction) {
private fun InsPOP(i: Instruction) {
val value = valueStack.pop()
when(ins.type!!) {
VmDataType.BYTE -> {
registers.setB(ins.reg1!!, value.toUByte())
}
VmDataType.WORD -> {
registers.setW(ins.reg1!!, value.toUShort())
}
}
setResultReg(i.reg1!!, value, i.type!!)
pc++
}
private fun InsSYSCALL(ins: Instruction) {
val call = Syscall.values()[ins.value!!]
private fun InsSYSCALL(i: Instruction) {
val call = Syscall.values()[i.value!!]
SysCalls.call(call, this)
pc++
}
@ -176,10 +183,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
private fun InsLOAD(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, i.value!!.toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, i.value!!.toUShort())
}
setResultReg(i.reg1!!, i.value!!, i.type!!)
pc++
}
@ -198,6 +202,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
pc++
}
private fun InsLOADX(i: Instruction) {
when (i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, memory.getB(i.value!! + registers.getW(i.reg2!!).toInt()))
@ -268,6 +273,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
pc++
}
private fun InsSTOREZX(i: Instruction) {
when (i.type!!) {
VmDataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt() + i.value!!, 0u)
@ -343,30 +349,6 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun getBranchOperands(i: Instruction): Pair<Int, Int> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg1!!).toInt(), registers.getB(i.reg2!!).toInt())
VmDataType.WORD -> Pair(registers.getW(i.reg1!!).toInt(), registers.getW(i.reg2!!).toInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
private fun getBranchOperandsU(i: Instruction): Pair<UInt, UInt> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg1!!).toUInt(), registers.getB(i.reg2!!).toUInt())
VmDataType.WORD -> Pair(registers.getW(i.reg1!!).toUInt(), registers.getW(i.reg2!!).toUInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
private fun getLogicalOperandsU(i: Instruction): Pair<UInt, UInt> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg2!!).toUInt(), registers.getB(i.reg3!!).toUInt())
VmDataType.WORD -> Pair(registers.getW(i.reg2!!).toUInt(), registers.getW(i.reg3!!).toUInt())
null -> throw IllegalArgumentException("need type for logical instruction")
}
}
private fun InsBNE(i: Instruction) {
val (left: Int, right: Int) = getBranchOperands(i)
if(left!=right)
@ -442,6 +424,78 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsSEQ(i: Instruction) {
val (resultReg: Int, left: Int, right: Int) = getSetOnConditionOperands(i)
val value = if(left==right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSNE(i: Instruction) {
val (resultReg: Int, left: Int, right: Int) = getSetOnConditionOperands(i)
val value = if(left!=right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSLT(i: Instruction) {
val (resultReg, left, right) = getSetOnConditionOperandsU(i)
val value = if(left<right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSLTS(i: Instruction) {
val (resultReg, left, right) = getSetOnConditionOperands(i)
val value = if(left<right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSGT(i: Instruction) {
val (resultReg, left, right) = getSetOnConditionOperandsU(i)
val value = if(left>right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSGTS(i: Instruction) {
val (resultReg, left, right) = getSetOnConditionOperands(i)
val value = if(left>right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSLE(i: Instruction) {
val (resultReg, left, right) = getSetOnConditionOperandsU(i)
val value = if(left<=right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSLES(i: Instruction) {
val (resultReg, left, right) = getSetOnConditionOperands(i)
val value = if(left<=right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSGE(i: Instruction) {
val (resultReg, left, right) = getSetOnConditionOperandsU(i)
val value = if(left>=right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsSGES(i: Instruction) {
val (resultReg, left, right) = getSetOnConditionOperands(i)
val value = if(left>=right) 1 else 0
setResultReg(resultReg, value, i.type!!)
pc++
}
private fun InsINC(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg1)+1u).toUByte())
@ -625,6 +679,18 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsSWAP(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
val value = registers.getW(i.reg2!!)
val newValue = value.toUByte()*256u + (value.toInt() ushr 8).toUInt()
registers.setW(i.reg1!!, newValue.toUShort())
}
VmDataType.WORD -> TODO("swap.w requires 32-bits registers")
}
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)
@ -654,16 +720,44 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsSWAP(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
val value = registers.getW(i.reg2!!)
val newValue = value.toUByte()*256u + (value.toInt() ushr 8).toUInt()
registers.setW(i.reg1!!, newValue.toUShort())
}
VmDataType.WORD -> TODO("swap.w requires 32-bits registers")
private fun getBranchOperands(i: Instruction): Pair<Int, Int> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg1!!).toInt(), registers.getB(i.reg2!!).toInt())
VmDataType.WORD -> Pair(registers.getW(i.reg1!!).toInt(), registers.getW(i.reg2!!).toInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
private fun getBranchOperandsU(i: Instruction): Pair<UInt, UInt> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg1!!).toUInt(), registers.getB(i.reg2!!).toUInt())
VmDataType.WORD -> Pair(registers.getW(i.reg1!!).toUInt(), registers.getW(i.reg2!!).toUInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
private fun getLogicalOperandsU(i: Instruction): Pair<UInt, UInt> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg2!!).toUInt(), registers.getB(i.reg3!!).toUInt())
VmDataType.WORD -> Pair(registers.getW(i.reg2!!).toUInt(), registers.getW(i.reg3!!).toUInt())
null -> throw IllegalArgumentException("need type for logical instruction")
}
}
private fun getSetOnConditionOperands(ins: Instruction): Triple<Int, Int, Int> {
return when(ins.type) {
VmDataType.BYTE -> Triple(ins.reg1!!, registers.getB(ins.reg2!!).toInt(), registers.getB(ins.reg3!!).toInt())
VmDataType.WORD -> Triple(ins.reg1!!, registers.getW(ins.reg2!!).toInt(), registers.getW(ins.reg3!!).toInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
private fun getSetOnConditionOperandsU(ins: Instruction): Triple<Int, UInt, UInt> {
return when(ins.type) {
VmDataType.BYTE -> Triple(ins.reg1!!, registers.getB(ins.reg2!!).toUInt(), registers.getB(ins.reg3!!).toUInt())
VmDataType.WORD -> Triple(ins.reg1!!, registers.getW(ins.reg2!!).toUInt(), registers.getW(ins.reg3!!).toUInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
pc++
}
private var window: GraphicsWindow? = null