adding singed integer datatypes

This commit is contained in:
Irmen de Jong 2018-10-11 09:54:26 +02:00
parent 2f7d03b6ab
commit 1fe5c943fd
11 changed files with 717 additions and 345 deletions

View File

@ -1,15 +1,39 @@
%zeropage full
%zpreserved $40,200
%zpreserved 100,250
%option enable_floats
~ main {
const uword width=320
const uword height=200
sub start() {
byte[3] barr1 = [-2,-1,2]
byte[3] barr2 = [-22,-1, 3]
ubyte[3] barr3 = [-2,-1,2] ; @ todo error
ubyte[3] barr4 = [1,2,33]
ubyte[3] barr5 = [1,2,-33] ; @todo error
word[3] warr1 = [-2,-1,2]
;word[3] warr2 = [-2,-1,2, 3453] ; @todo ok
uword[3] warr3 = [-2,-1,2]
uword[3] warr4 = [1,2,33.w]
uword[3] warr5 = [1,2,-33] ; @todo error
byte b1 = 50 * 2
byte b2 = -50 * 2
ubyte ub1 = 50 * 2
word w1 = 999 * 2
word w2 = -999 * 2
uword uw1 = 999 * 2
float f1 = 999*2
float f2 = -999*2
return
}
sub toscreenx(x: float, z: float) -> word {
return floor(x/(4.2+z) * flt(height)) + width // 2
}
}

View File

@ -1062,6 +1062,70 @@ class LiteralValue(val type: DataType,
throw ExpressionError("cannot compare type $type with ${other.type}", other.position)
}
fun intoDatatype(targettype: DataType): LiteralValue {
if(type==targettype)
return this
when(type) {
DataType.UBYTE -> {
if(targettype==DataType.BYTE && bytevalue!! <= 127)
return LiteralValue(targettype, bytevalue=bytevalue, position=position)
if(targettype==DataType.WORD || targettype==DataType.UWORD)
return LiteralValue(targettype, wordvalue=bytevalue!!.toInt(), position=position)
if(targettype==DataType.FLOAT)
return LiteralValue(targettype, floatvalue=bytevalue!!.toDouble(), position=position)
}
DataType.BYTE -> {
if(targettype==DataType.UBYTE && bytevalue!! >= 0)
return LiteralValue(targettype, bytevalue=bytevalue, position=position)
if(targettype==DataType.UWORD && bytevalue!! >= 0)
return LiteralValue(targettype, wordvalue=bytevalue.toInt(), position=position)
if(targettype==DataType.WORD)
return LiteralValue(targettype, wordvalue=bytevalue!!.toInt(), position=position)
if(targettype==DataType.FLOAT)
return LiteralValue(targettype, floatvalue=bytevalue!!.toDouble(), position=position)
}
DataType.UWORD -> {
if(targettype==DataType.BYTE && wordvalue!! <= 127)
return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position)
if(targettype==DataType.UBYTE && wordvalue!! <= 255)
return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position)
if(targettype==DataType.WORD && wordvalue!! <= 32767)
return LiteralValue(targettype, wordvalue=wordvalue, position=position)
if(targettype==DataType.FLOAT)
return LiteralValue(targettype, floatvalue=wordvalue!!.toDouble(), position=position)
}
DataType.WORD -> {
if(targettype==DataType.BYTE && wordvalue!! in -128..127)
return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position)
if(targettype==DataType.UBYTE && wordvalue!! in 0..255)
return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position)
if(targettype==DataType.UWORD && wordvalue!! >=0)
return LiteralValue(targettype, wordvalue=wordvalue, position=position)
if(targettype==DataType.FLOAT)
return LiteralValue(targettype, floatvalue=wordvalue!!.toDouble(), position=position)
}
DataType.FLOAT -> {
if(floor(floatvalue!!)==floatvalue) {
val value = floatvalue.toInt()
if (targettype == DataType.BYTE && value in -128..127)
return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
if (targettype == DataType.UBYTE && value in 0..255)
return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
if (targettype == DataType.WORD && value in -32768..32767)
return LiteralValue(targettype, wordvalue = value, position = position)
if (targettype == DataType.UWORD && value in 0..65535)
return LiteralValue(targettype, wordvalue = value, position = position)
}
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
if(targettype in StringDatatypes)
return this
}
else -> {}
}
throw FatalAstException("invalid type conversion from $this to $targettype")
}
}

View File

@ -105,10 +105,16 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He
if(scope !in vardeclsToAdd)
vardeclsToAdd[scope] = mutableListOf()
vardeclsToAdd[scope]!!.add(decl.asDefaultValueDecl())
val declvalue = decl.value!!
val value =
if(declvalue is LiteralValue)
declvalue.intoDatatype(decl.datatype)
else
declvalue
return VariableInitializationAssignment(
AssignTarget(null, IdentifierReference(decl.scopedname.split("."), decl.position), null, decl.position),
null,
decl.value!!,
value,
decl.position
)
}

View File

@ -200,102 +200,126 @@ class StackVmProgram(val name: String, val heap: HeapValues) {
private fun optimizeDataConversionAndUselessDiscards() {
// - push value followed by a data type conversion -> push the value in the correct type and remove the conversion
// - push something followed by a discard -> remove both
val instructionsToReplace = mutableMapOf<Int, Instruction>()
fun optimizeDiscardAfterPush(index0: Int, index1: Int, ins1: Instruction) {
if (ins1.opcode == Opcode.DISCARD_FLOAT || ins1.opcode == Opcode.DISCARD_WORD || ins1.opcode == Opcode.DISCARD_BYTE) {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
}
fun optimizeFloatConversion(index0: Int, index1: Int, ins1: Instruction) {
when (ins1.opcode) {
Opcode.LSB,
Opcode.MSB,
Opcode.B2WORD,
Opcode.UB2UWORD,
Opcode.MSB2WORD,
Opcode.B2FLOAT,
Opcode.UB2FLOAT,
Opcode.UW2FLOAT,
Opcode.W2FLOAT -> throw CompilerException("invalid conversion following a float")
Opcode.DISCARD_FLOAT -> {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_BYTE, Opcode.DISCARD_WORD -> throw CompilerException("invalid discard type following a float")
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}")
}
}
fun optimizeWordConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
when (ins1.opcode) {
Opcode.LSB -> {
val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, ins0.arg!!.integerValue() and 255))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.MSB -> {
val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, ins0.arg!!.integerValue() ushr 8 and 255))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.B2WORD,
Opcode.UB2UWORD,
Opcode.MSB2WORD,
Opcode.B2FLOAT,
Opcode.UB2FLOAT -> throw CompilerException("invalid conversion following a word")
Opcode.W2FLOAT, Opcode.UW2FLOAT -> {
val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_WORD -> {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_BYTE, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}")
}
}
fun optimizeByteConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
when (ins1.opcode) {
Opcode.LSB -> instructionsToReplace[index1] = Instruction(Opcode.NOP)
Opcode.MSB -> throw CompilerException("msb of a byte")
Opcode.UB2UWORD -> {
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, ins0.arg!!.integerValue()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.B2WORD -> {
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, ins0.arg!!.integerValue()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.MSB2WORD -> {
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 256 * ins0.arg!!.integerValue()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.B2FLOAT, Opcode.UB2FLOAT -> {
val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.W2FLOAT, Opcode.UW2FLOAT -> throw CompilerException("invalid conversion following a byte")
Opcode.DISCARD_BYTE -> {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}")
}
}
val typeConversionOpcodes = setOf(
Opcode.LSB,
Opcode.MSB,
Opcode.B2WORD,
Opcode.UB2UWORD,
Opcode.MSB2WORD,
Opcode.B2FLOAT,
Opcode.UB2FLOAT,
Opcode.W2FLOAT,
Opcode.UW2FLOAT,
Opcode.DISCARD_BYTE,
Opcode.DISCARD_WORD,
Opcode.DISCARD_FLOAT
)
val instructionsToReplace = mutableMapOf<Int, Instruction>()
this.instructions.asSequence().withIndex().windowed(2).toList().forEach {
if(it[1].value.opcode in typeConversionOpcodes) {
when(it[0].value.opcode) {
Opcode.PUSH_BYTE -> when (it[1].value.opcode) {
Opcode.LSB -> instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
Opcode.MSB -> throw CompilerException("msb of a byte")
Opcode.B2WORD -> {
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, it[0].value.arg!!.integerValue()))
instructionsToReplace[it[0].index] = ins
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.MSB2WORD -> {
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 256 * it[0].value.arg!!.integerValue()))
instructionsToReplace[it[0].index] = ins
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.B2FLOAT -> {
val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, it[0].value.arg!!.integerValue().toDouble()))
instructionsToReplace[it[0].index] = ins
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.W2FLOAT -> throw CompilerException("invalid conversion following a byte")
Opcode.DISCARD_BYTE -> {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
else -> {}
}
Opcode.PUSH_WORD -> when (it[1].value.opcode) {
Opcode.LSB -> {
val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, it[0].value.arg!!.integerValue() and 255))
instructionsToReplace[it[0].index] = ins
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.MSB -> {
val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, it[0].value.arg!!.integerValue() ushr 8 and 255))
instructionsToReplace[it[0].index] = ins
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.B2WORD,
Opcode.MSB2WORD,
Opcode.B2FLOAT -> throw CompilerException("invalid conversion following a word")
Opcode.W2FLOAT -> {
val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, it[0].value.arg!!.integerValue().toDouble()))
instructionsToReplace[it[0].index] = ins
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_WORD -> {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_BYTE, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
else -> {}
}
Opcode.PUSH_FLOAT -> when (it[1].value.opcode) {
Opcode.LSB,
Opcode.MSB,
Opcode.B2WORD,
Opcode.MSB2WORD,
Opcode.B2FLOAT,
Opcode.W2FLOAT -> throw CompilerException("invalid conversion following a float")
Opcode.DISCARD_FLOAT -> {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_BYTE, Opcode.DISCARD_WORD -> throw CompilerException("invalid discard type following a float")
else -> {}
}
Opcode.PUSH_BYTE -> optimizeByteConversion(it[0].index, it[0].value, it[1].index, it[1].value)
Opcode.PUSH_WORD -> optimizeWordConversion(it[0].index, it[0].value, it[1].index, it[1].value)
Opcode.PUSH_FLOAT -> optimizeFloatConversion(it[0].index, it[1].index, it[1].value)
Opcode.PUSH_VAR_FLOAT,
Opcode.PUSH_VAR_WORD,
Opcode.PUSH_VAR_BYTE,
Opcode.PUSH_MEM_BYTE,
Opcode.PUSH_MEM_WORD,
Opcode.PUSH_MEM_FLOAT -> when (it[1].value.opcode) {
Opcode.DISCARD_FLOAT, Opcode.DISCARD_WORD, Opcode.DISCARD_BYTE -> {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
else -> {}
}
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB,
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW,
Opcode.PUSH_MEM_FLOAT -> optimizeDiscardAfterPush(it[0].index, it[1].index, it[1].value)
else -> {}
}
}
@ -817,9 +841,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
else -> {
val lv = expr.constValue(namespace, heap) ?: throw CompilerException("constant expression required, not $expr")
when(lv.type) {
DataType.UBYTE, DataType.BYTE -> stackvmProg.instr(Opcode.PUSH_BYTE, Value(DataType.UBYTE, lv.bytevalue!!))
DataType.UWORD, DataType.WORD -> stackvmProg.instr(Opcode.PUSH_WORD, Value(DataType.UWORD, lv.wordvalue!!))
DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, lv.floatvalue!!))
DataType.UBYTE, DataType.BYTE -> stackvmProg.instr(Opcode.PUSH_BYTE, Value(lv.type, lv.bytevalue!!))
DataType.UWORD, DataType.WORD -> stackvmProg.instr(Opcode.PUSH_WORD, Value(lv.type, lv.wordvalue!!))
DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH_FLOAT, Value(lv.type, lv.floatvalue!!))
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
if(lv.heapId==null)
throw CompilerException("string should have been moved into heap ${lv.position}")
@ -847,7 +871,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
when(givenDt) {
DataType.UBYTE -> when(targetDt) {
DataType.UWORD, DataType.WORD -> stackvmProg.instr(Opcode.UB2UWORD)
DataType.FLOAT -> stackvmProg.instr(Opcode.B2FLOAT)
DataType.FLOAT -> stackvmProg.instr(Opcode.UB2FLOAT)
else -> {}
}
DataType.BYTE -> when(targetDt) {
@ -883,8 +907,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
throw CompilerException("const ref should have been const-folded away")
VarDeclType.MEMORY -> {
when (target.datatype) {
DataType.UBYTE -> stackvmProg.instr(Opcode.PUSH_MEM_BYTE, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.UWORD -> stackvmProg.instr(Opcode.PUSH_MEM_WORD, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.UBYTE -> stackvmProg.instr(Opcode.PUSH_MEM_UB, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.BYTE-> stackvmProg.instr(Opcode.PUSH_MEM_B, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.UWORD -> stackvmProg.instr(Opcode.PUSH_MEM_UW, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.WORD -> stackvmProg.instr(Opcode.PUSH_MEM_W, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH_MEM_FLOAT, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
else -> TODO("invalid datatype for memory variable expression: $target")
}
@ -929,8 +955,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
// 1 argument, type determines the exact opcode to use
val arg = args.single()
when (arg.resultingDatatype(namespace, heap)) {
DataType.UBYTE -> stackvmProg.instr(Opcode.B2FLOAT)
DataType.UWORD -> stackvmProg.instr(Opcode.W2FLOAT)
DataType.UBYTE -> stackvmProg.instr(Opcode.UB2FLOAT)
DataType.BYTE -> stackvmProg.instr(Opcode.B2FLOAT)
DataType.UWORD -> stackvmProg.instr(Opcode.UW2FLOAT)
DataType.WORD -> stackvmProg.instr(Opcode.W2FLOAT)
DataType.FLOAT -> stackvmProg.instr(Opcode.NOP)
else -> throw CompilerException("wrong datatype for flt()")
}

View File

@ -35,13 +35,13 @@ val BuiltinFunctions = mapOf(
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toDegrees) },
"avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), DataType.FLOAT, ::builtinAvg),
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.FLOAT, ::builtinAbs),
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::round) }, // type depends on arg
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::floor) }, // type depends on arg
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::ceil) }, // type depends on arg
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::round) },
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::floor) },
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::ceil) },
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.max()!! }}, // type depends on args
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.min()!! }}, // type depends on args
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.sum() }}, // type depends on args
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes.toList())), null, ::builtinLen), // type depends on args
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes.toList())), DataType.UWORD, ::builtinLen),
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }},
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }},
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x and 255 }},
@ -142,17 +142,6 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.MATRIX_B -> DataType.BYTE
}
}
"round", "floor", "ceil" -> {
val dt=args.single().resultingDatatype(namespace, heap)
when(dt) {
DataType.UBYTE -> DataType.UBYTE
DataType.BYTE -> DataType.BYTE
DataType.UWORD -> DataType.UWORD
DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.WORD
else -> null
}
}
"sum" -> {
val dt=datatypeFromListArg(args.single())
when(dt) {
@ -167,27 +156,6 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.UWORD
}
}
"len" -> {
// len of a str is always 0..255 so always a byte,
// len of other things is assumed to need a word (even though the actual length could be less than 256)
val arg = args.single()
when(arg) {
is IdentifierReference -> {
val stmt = arg.targetStatement(namespace)
when(stmt) {
is VarDecl -> {
val value = stmt.value
if(value is LiteralValue) {
if(value.isString) return DataType.UBYTE // strings are 0..255
}
}
}
DataType.UWORD // assume other lengths are words for now.
}
is LiteralValue -> throw FatalAstException("len of literalvalue should have been const-folded away already")
else -> DataType.UWORD
}
}
else -> throw FatalAstException("unknown result type for builtin function $function")
}
}
@ -207,15 +175,13 @@ private fun oneDoubleArg(args: List<IExpression>, position: Position, namespace:
return numericLiteral(function(float), args[0].position)
}
private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue {
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val float: Double = when(constval.type) {
DataType.UBYTE, DataType.UWORD, DataType.FLOAT -> constval.asNumericValue!!.toDouble()
else -> throw SyntaxError("built-in function requires one floating point argument", position)
}
return numericLiteral(function(float).toInt(), args[0].position)
if(constval.type!=DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
return LiteralValue(DataType.WORD, wordvalue=function(constval.asNumericValue!!.toDouble()).toInt(), position=args[0].position)
}
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Int)->Number): LiteralValue {
@ -384,15 +350,15 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
return when(argument.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.MATRIX_UB, DataType.MATRIX_B -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).array!!.size
numericLiteral(arraySize, args[0].position)
LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position)
}
DataType.ARRAY_F -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).doubleArray!!.size
numericLiteral(arraySize, args[0].position)
LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position)
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
val str = argument.strvalue ?: heap.get(argument.heapId!!).str!!
numericLiteral(str.length, args[0].position)
LiteralValue(DataType.UWORD, wordvalue=str.length, position=args[0].position)
}
DataType.UBYTE, DataType.BYTE,
DataType.UWORD, DataType.WORD,

View File

@ -44,7 +44,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
}
}
in IntegerDatatypes -> {
// vardecl: for byte/word vars, convert char/string of length 1 initialization values to integer
// vardecl: for byte/word vars, convert char/string of length 1 initialization values to ubyte integer
val literal = decl.value as? LiteralValue
if (literal != null && literal.isString && literal.strvalue?.length == 1) {
val petscii = Petscii.encodePetscii(literal.strvalue)[0]
@ -426,7 +426,6 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
return super.process(range)
}
// todo datatypes
override fun process(literalValue: LiteralValue): LiteralValue {
if(literalValue.strvalue!=null) {
// intern the string; move it into the heap
@ -439,11 +438,23 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
}
} else if(literalValue.arrayvalue!=null) {
val newArray = literalValue.arrayvalue.map { it.process(this) }.toTypedArray()
var arrayDt = DataType.ARRAY_UB
if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.UWORD })
arrayDt = DataType.ARRAY_UW
if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.FLOAT })
arrayDt = DataType.ARRAY_F
val arrayDt =
if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.FLOAT })
DataType.ARRAY_F
else {
if (newArray.any { it.resultingDatatype(namespace, heap) in setOf(DataType.BYTE, DataType.WORD) }) {
// we have signed values
if (newArray.any { it.resultingDatatype(namespace, heap) == DataType.WORD })
DataType.ARRAY_W
else
DataType.ARRAY_B
} else {
// only unsigned values
if (newArray.any { it.resultingDatatype(namespace, heap) == DataType.UWORD })
DataType.ARRAY_UW
else DataType.ARRAY_UB
}
}
// if the values are all constants, the array is moved to the heap
val allElementsAreConstant = newArray.fold(true) { c, expr-> c and (expr is LiteralValue)}
@ -454,10 +465,21 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
return super.process(literalValue)
}
if(arrayDt==DataType.ARRAY_UB || arrayDt==DataType.MATRIX_UB) {
// all values should be ubytes
val integerArray = litArray.map { (it as LiteralValue).bytevalue }
if (integerArray.any { it == null || it.toInt() !in 0..255 }) {
addError(ExpressionError("byte array elements must all be integers 0..255", literalValue.position))
return super.process(literalValue)
}
val array = integerArray.mapNotNull { it?.toInt() }.toIntArray()
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(arrayDt, heapId = heapId, position = literalValue.position)
return super.process(newValue)
} else if(arrayDt==DataType.ARRAY_B || arrayDt==DataType.MATRIX_B) {
// all values should be bytes
val integerArray = litArray.map { (it as LiteralValue).bytevalue }
if(integerArray.any { it==null || it.toInt() !in 0..255 }) {
addError(ExpressionError("byte array elements must all be integers 0..255", literalValue.position))
if(integerArray.any { it==null || it.toInt() !in -128..127 }) {
addError(ExpressionError("byte array elements must all be integers -128..127", literalValue.position))
return super.process(literalValue)
}
val array = integerArray.mapNotNull { it?.toInt() }.toIntArray()
@ -475,6 +497,17 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(DataType.ARRAY_UW, heapId=heapId, position = literalValue.position)
return super.process(newValue)
} else if(arrayDt==DataType.ARRAY_W) {
// all values should be bytes or words
val integerArray = litArray.map { (it as LiteralValue).asIntegerValue }
if(integerArray.any {it==null || it !in -32768..32767 }) {
addError(ExpressionError("word array elements must all be integers -32768..32767", literalValue.position))
return super.process(literalValue)
}
val array = integerArray.filterNotNull().toIntArray()
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(DataType.ARRAY_W, heapId=heapId, position = literalValue.position)
return super.process(newValue)
} else if(arrayDt==DataType.ARRAY_F) {
// all values should be bytes, words or floats
val doubleArray = litArray.map { (it as LiteralValue).asNumericValue?.toDouble() }

View File

@ -2,29 +2,62 @@ package prog8.stackvm
import prog8.compiler.target.c64.Mflpt5
import prog8.compiler.target.c64.Petscii
import kotlin.math.abs
class Memory {
private val mem = ShortArray(65536) // shorts because byte is signed and we store values 0..255
fun getByte(address: Int): Short {
fun getUByte(address: Int): Short {
return mem[address]
}
fun setByte(address: Int, value: Short) {
if(value<0 || value>255) throw VmExecutionException("byte value not 0..255")
fun getSByte(address: Int): Short {
val ubyte = getUByte(address)
if(ubyte <= 127)
return ubyte
return (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement
}
fun setUByte(address: Int, value: Short) {
if(value !in 0..255)
throw VmExecutionException("ubyte value out of range")
mem[address] = value
}
fun getWord(address: Int): Int {
fun setSByte(address: Int, value: Short) {
if(value !in -128..127) throw VmExecutionException("byte value out of range")
if(value>=0)
setUByte(address, value)
else
setUByte(address, ((abs(value.toInt()) xor 255)+1).toShort()) // 2's complement
}
fun getUWord(address: Int): Int {
return mem[address] + 256*mem[address+1]
}
fun setWord(address: Int, value: Int) {
if(value<0 || value>65535) throw VmExecutionException("word value not 0..65535")
fun getSWord(address: Int): Int {
val uword = getUWord(address)
if(uword <= 32767)
return uword
return -((uword xor 65535)+1) // 2's complement
}
fun setUWord(address: Int, value: Int) {
if(value !in 0..65535)
throw VmExecutionException("uword value out of range")
mem[address] = value.and(255).toShort()
mem[address+1] = (value / 256).toShort()
}
fun setSWord(address: Int, value: Int) {
if(value !in -32768..32767) throw VmExecutionException("word value out of range")
if(value>=0)
setUWord(address, value)
else
setUWord(address, (abs(value) xor 65535)+1) // 2's complement
}
fun setFloat(address: Int, value: Double) {
val mflpt5 = Mflpt5.fromNumber(value)
mem[address] = mflpt5.b0

View File

@ -142,8 +142,10 @@ class Program (val name: String,
}
val (type, valueStr) = args.split(':')
return when(type) {
"b" -> Value(DataType.UBYTE, valueStr.toShort(16))
"w" -> Value(DataType.UWORD, valueStr.toInt(16))
"b" -> Value(DataType.BYTE, valueStr.toShort(16))
"ub" -> Value(DataType.UBYTE, valueStr.toShort(16))
"w" -> Value(DataType.WORD, valueStr.toInt(16))
"uw" -> Value(DataType.UWORD, valueStr.toInt(16))
"f" -> Value(DataType.FLOAT, valueStr.toDouble())
"heap" -> {
val heapId = valueStr.toInt()
@ -165,9 +167,9 @@ class Program (val name: String,
throw VmExecutionException("missing value type character")
val type = DataType.valueOf(typeStr.toUpperCase())
val value = when(type) {
DataType.UBYTE -> Value(DataType.UBYTE, valueStr.substring(2).toShort(16))
DataType.UBYTE -> Value(DataType.UBYTE, valueStr.substring(3).toShort(16))
DataType.BYTE -> Value(DataType.BYTE, valueStr.substring(2).toShort(16))
DataType.UWORD -> Value(DataType.UWORD, valueStr.substring(2).toInt(16))
DataType.UWORD -> Value(DataType.UWORD, valueStr.substring(3).toInt(16))
DataType.WORD -> Value(DataType.WORD, valueStr.substring(2).toInt(16))
DataType.FLOAT -> Value(DataType.FLOAT, valueStr.substring(2).toDouble())
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {

View File

@ -1,6 +1,6 @@
package prog8.stackvm
import prog8.ast.DataType
import prog8.ast.*
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.Petscii
import java.io.File
@ -14,22 +14,26 @@ enum class Opcode {
PUSH_BYTE, // push byte value
PUSH_WORD, // push word value (or 'address' of string / array / matrix)
PUSH_FLOAT, // push float value
PUSH_MEM_BYTE, // push byte value from memory to stack
PUSH_MEM_WORD, // push word value from memory to stack
PUSH_MEM_B, // push byte value from memory to stack
PUSH_MEM_UB, // push byte value from memory to stack
PUSH_MEM_W, // push word value from memory to stack
PUSH_MEM_UW, // push word value from memory to stack
PUSH_MEM_FLOAT, // push float value from memory to stack
PUSH_VAR_BYTE, // push byte variable
PUSH_VAR_WORD, // push word variable
PUSH_VAR_BYTE, // push byte variable (ubyte, byte)
PUSH_VAR_WORD, // push word variable (uword, word)
PUSH_VAR_FLOAT, // push float variable
// popping values off the (evaluation) stack, possibly storing them in another location
DISCARD_BYTE, // discard top byte value
DISCARD_WORD, // discard top word value
DISCARD_FLOAT, // discard top float value
POP_MEM_BYTE, // pop byte value into destination memory address
POP_MEM_WORD, // pop word value into destination memory address
POP_MEM_B, // pop byte value into destination memory address
POP_MEM_UB, // pop byte value into destination memory address
POP_MEM_W, // pop word value into destination memory address
POP_MEM_UW, // pop word value into destination memory address
POP_MEM_FLOAT, // pop float value into destination memory address
POP_VAR_BYTE, // pop byte value into variable
POP_VAR_WORD, // pop word value into variable
POP_VAR_BYTE, // pop byte value into variable (byte, ubyte)
POP_VAR_WORD, // pop word value into variable (word, uword)
POP_VAR_FLOAT, // pop float value into variable
// optimized copying of one var to another (replaces push+pop)
@ -433,11 +437,19 @@ class StackVm(private var traceOutputFile: String?) {
for (value in meminit.value) {
when(value.type) {
DataType.UBYTE -> {
mem.setByte(address, value.integerValue().toShort())
mem.setUByte(address, value.integerValue().toShort())
address += 1
}
DataType.BYTE -> {
mem.setSByte(address, value.integerValue().toShort())
address += 1
}
DataType.UWORD -> {
mem.setWord(address, value.integerValue())
mem.setUWord(address, value.integerValue())
address += 2
}
DataType.WORD -> {
mem.setSWord(address, value.integerValue())
address += 2
}
DataType.FLOAT -> {
@ -474,24 +486,32 @@ class StackVm(private var traceOutputFile: String?) {
when (ins.opcode) {
Opcode.NOP -> {}
Opcode.PUSH_BYTE -> {
checkDt(ins.arg, DataType.UBYTE)
checkDt(ins.arg, setOf(DataType.UBYTE, DataType.BYTE))
evalstack.push(ins.arg)
}
Opcode.PUSH_WORD -> {
checkDt(ins.arg, setOf(DataType.UWORD, DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.MATRIX_UB))
checkDt(ins.arg, setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes)
evalstack.push(ins.arg)
}
Opcode.PUSH_FLOAT -> {
checkDt(ins.arg, DataType.FLOAT)
evalstack.push(ins.arg)
}
Opcode.PUSH_MEM_BYTE -> {
Opcode.PUSH_MEM_UB -> {
val address = ins.arg!!.integerValue()
evalstack.push(Value(DataType.UBYTE, mem.getByte(address)))
evalstack.push(Value(DataType.UBYTE, mem.getUByte(address)))
}
Opcode.PUSH_MEM_WORD -> {
Opcode.PUSH_MEM_B -> {
val address = ins.arg!!.integerValue()
evalstack.push(Value(DataType.UWORD, mem.getWord(address)))
evalstack.push(Value(DataType.BYTE, mem.getSByte(address)))
}
Opcode.PUSH_MEM_UW -> {
val address = ins.arg!!.integerValue()
evalstack.push(Value(DataType.UWORD, mem.getUWord(address)))
}
Opcode.PUSH_MEM_W -> {
val address = ins.arg!!.integerValue()
evalstack.push(Value(DataType.WORD, mem.getSWord(address)))
}
Opcode.PUSH_MEM_FLOAT -> {
val address = ins.arg!!.integerValue()
@ -509,17 +529,29 @@ class StackVm(private var traceOutputFile: String?) {
val value = evalstack.pop()
checkDt(value, DataType.FLOAT)
}
Opcode.POP_MEM_BYTE -> {
Opcode.POP_MEM_UB -> {
val value = evalstack.pop()
checkDt(value, DataType.UBYTE)
val address = ins.arg!!.integerValue()
mem.setByte(address, value.integerValue().toShort())
mem.setUByte(address, value.integerValue().toShort())
}
Opcode.POP_MEM_WORD -> {
Opcode.POP_MEM_B -> {
val value = evalstack.pop()
checkDt(value, DataType.BYTE)
val address = ins.arg!!.integerValue()
mem.setSByte(address, value.integerValue().toShort())
}
Opcode.POP_MEM_UW -> {
val value = evalstack.pop()
checkDt(value, DataType.UWORD)
val address = ins.arg!!.integerValue()
mem.setWord(address, value.integerValue())
mem.setUWord(address, value.integerValue())
}
Opcode.POP_MEM_W -> {
val value = evalstack.pop()
checkDt(value, DataType.WORD)
val address = ins.arg!!.integerValue()
mem.setSWord(address, value.integerValue())
}
Opcode.POP_MEM_FLOAT -> {
val value = evalstack.pop()
@ -539,6 +571,18 @@ class StackVm(private var traceOutputFile: String?) {
checkDt(second, DataType.UWORD)
evalstack.push(second.add(top))
}
Opcode.ADD_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(second.add(top))
}
Opcode.ADD_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(second.add(top))
}
Opcode.ADD_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
@ -557,6 +601,18 @@ class StackVm(private var traceOutputFile: String?) {
checkDt(second, DataType.UWORD)
evalstack.push(second.sub(top))
}
Opcode.SUB_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(second.sub(top))
}
Opcode.SUB_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(second.sub(top))
}
Opcode.SUB_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
@ -575,6 +631,18 @@ class StackVm(private var traceOutputFile: String?) {
checkDt(second, DataType.UWORD)
evalstack.push(second.mul(top))
}
Opcode.MUL_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(second.mul(top))
}
Opcode.MUL_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(second.mul(top))
}
Opcode.MUL_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
@ -593,6 +661,18 @@ class StackVm(private var traceOutputFile: String?) {
checkDt(second, DataType.UWORD)
evalstack.push(second.div(top))
}
Opcode.DIV_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(second.div(top))
}
Opcode.DIV_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(second.div(top))
}
Opcode.DIV_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
@ -611,6 +691,18 @@ class StackVm(private var traceOutputFile: String?) {
checkDt(second, DataType.UWORD)
evalstack.push(second.floordiv(top))
}
Opcode.FLOORDIV_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(second.floordiv(top))
}
Opcode.FLOORDIV_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(second.floordiv(top))
}
Opcode.FLOORDIV_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
@ -629,6 +721,18 @@ class StackVm(private var traceOutputFile: String?) {
checkDt(second, DataType.UWORD)
evalstack.push(second.remainder(top))
}
Opcode.REMAINDER_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(second.remainder(top))
}
Opcode.REMAINDER_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(second.remainder(top))
}
Opcode.REMAINDER_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
@ -647,6 +751,18 @@ class StackVm(private var traceOutputFile: String?) {
checkDt(second, DataType.UWORD)
evalstack.push(second.pow(top))
}
Opcode.POW_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(second.pow(top))
}
Opcode.POW_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(second.pow(top))
}
Opcode.POW_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
@ -655,12 +771,12 @@ class StackVm(private var traceOutputFile: String?) {
}
Opcode.NEG_B -> {
val v = evalstack.pop()
checkDt(v, DataType.UBYTE)
checkDt(v, DataType.BYTE)
evalstack.push(v.neg())
}
Opcode.NEG_W -> {
val v = evalstack.pop()
checkDt(v, DataType.UWORD)
checkDt(v, DataType.WORD)
evalstack.push(v.neg())
}
Opcode.NEG_F -> {
@ -839,82 +955,85 @@ class StackVm(private var traceOutputFile: String?) {
Opcode.CLI -> P_irqd = false
Opcode.TERMINATE -> throw VmTerminationException("terminate instruction")
Opcode.BREAKPOINT -> throw VmBreakpointException()
Opcode.LINE -> {
sourceLine = ins.callLabel!!
}
Opcode.SHL_MEM_BYTE -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UBYTE, mem.getByte(addr))
val value = Value(DataType.UBYTE, mem.getUByte(addr))
val newValue = value.shl()
mem.setByte(addr, newValue.integerValue().toShort())
mem.setUByte(addr, newValue.integerValue().toShort())
}
Opcode.SHL_MEM_WORD -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UWORD, mem.getWord(addr))
val value = Value(DataType.UWORD, mem.getUWord(addr))
val newValue = value.shl()
mem.setWord(addr, newValue.integerValue())
mem.setUWord(addr, newValue.integerValue())
}
Opcode.SHR_MEM_BYTE -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UBYTE, mem.getByte(addr))
val value = Value(DataType.UBYTE, mem.getUByte(addr))
val newValue = value.shr()
mem.setByte(addr, newValue.integerValue().toShort())
mem.setUByte(addr, newValue.integerValue().toShort())
}
Opcode.SHR_MEM_WORD -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UWORD, mem.getWord(addr))
val value = Value(DataType.UWORD, mem.getUWord(addr))
val newValue = value.shr()
mem.setWord(addr, newValue.integerValue())
mem.setUWord(addr, newValue.integerValue())
}
Opcode.ROL_MEM_BYTE -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UBYTE, mem.getByte(addr))
val value = Value(DataType.UBYTE, mem.getUByte(addr))
val (newValue, newCarry) = value.rol(P_carry)
mem.setByte(addr, newValue.integerValue().toShort())
mem.setUByte(addr, newValue.integerValue().toShort())
P_carry = newCarry
}
Opcode.ROL_MEM_WORD -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UWORD, mem.getWord(addr))
val value = Value(DataType.UWORD, mem.getUWord(addr))
val (newValue, newCarry) = value.rol(P_carry)
mem.setWord(addr, newValue.integerValue())
mem.setUWord(addr, newValue.integerValue())
P_carry = newCarry
}
Opcode.ROR_MEM_BYTE -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UBYTE, mem.getByte(addr))
val value = Value(DataType.UBYTE, mem.getUByte(addr))
val (newValue, newCarry) = value.ror(P_carry)
mem.setByte(addr, newValue.integerValue().toShort())
mem.setUByte(addr, newValue.integerValue().toShort())
P_carry = newCarry
}
Opcode.ROR_MEM_WORD -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UWORD, mem.getWord(addr))
val value = Value(DataType.UWORD, mem.getUWord(addr))
val (newValue, newCarry) = value.ror(P_carry)
mem.setWord(addr, newValue.integerValue())
mem.setUWord(addr, newValue.integerValue())
P_carry = newCarry
}
Opcode.ROL2_MEM_BYTE -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UBYTE, mem.getByte(addr))
val value = Value(DataType.UBYTE, mem.getUByte(addr))
val newValue = value.rol2()
mem.setByte(addr, newValue.integerValue().toShort())
mem.setUByte(addr, newValue.integerValue().toShort())
}
Opcode.ROL2_MEM_WORD -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UWORD, mem.getWord(addr))
val value = Value(DataType.UWORD, mem.getUWord(addr))
val newValue = value.rol2()
mem.setWord(addr, newValue.integerValue())
mem.setUWord(addr, newValue.integerValue())
}
Opcode.ROR2_MEM_BYTE -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UBYTE, mem.getByte(addr))
val value = Value(DataType.UBYTE, mem.getUByte(addr))
val newValue = value.ror2()
mem.setByte(addr, newValue.integerValue().toShort())
mem.setUByte(addr, newValue.integerValue().toShort())
}
Opcode.ROR2_MEM_WORD -> {
val addr = ins.arg!!.integerValue()
val value = Value(DataType.UWORD, mem.getWord(addr))
val value = Value(DataType.UWORD, mem.getUWord(addr))
val newValue = value.ror2()
mem.setWord(addr, newValue.integerValue())
mem.setUWord(addr, newValue.integerValue())
}
Opcode.JUMP -> {} // do nothing; the next instruction is wired up already to the jump target
@ -940,7 +1059,7 @@ class StackVm(private var traceOutputFile: String?) {
}
Opcode.PUSH_VAR_BYTE -> {
val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(value, DataType.UBYTE)
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE))
evalstack.push(value)
}
Opcode.PUSH_VAR_WORD -> {
@ -955,30 +1074,38 @@ class StackVm(private var traceOutputFile: String?) {
}
Opcode.POP_VAR_BYTE -> {
val value = evalstack.pop()
checkDt(value, DataType.UBYTE)
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE))
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(variable, DataType.UBYTE)
checkDt(variable, setOf(DataType.UBYTE, DataType.BYTE))
if(value.type!=variable.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel!!] = value
}
Opcode.POP_VAR_WORD -> {
val value = evalstack.pop()
checkDt(value, setOf(DataType.UWORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(value, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(variable, setOf(DataType.UWORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(variable, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
if(value.type!=variable.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel!!] = value
}
Opcode.COPY_VAR_BYTE -> {
val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}")
checkDt(source, DataType.UBYTE)
checkDt(dest, DataType.UBYTE)
checkDt(source, setOf(DataType.UBYTE, DataType.BYTE))
checkDt(dest, setOf(DataType.UBYTE, DataType.BYTE))
if(dest.type!=source.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel2!!] = source
}
Opcode.COPY_VAR_WORD -> {
val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}")
checkDt(source, setOf(DataType.UWORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(dest, setOf(DataType.UWORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(source, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(dest, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
if(dest.type!=source.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel2!!] = source
}
Opcode.COPY_VAR_FLOAT -> {
@ -1327,8 +1454,13 @@ class StackVm(private var traceOutputFile: String?) {
}
Opcode.B2WORD -> {
val byte = evalstack.pop()
checkDt(byte, DataType.UBYTE)
evalstack.push(Value(DataType.UWORD, byte.integerValue()))
checkDt(byte, DataType.BYTE)
evalstack.push(Value(DataType.WORD, byte.integerValue()))
}
Opcode.UB2UWORD -> {
val ubyte = evalstack.pop()
checkDt(ubyte, DataType.UBYTE)
evalstack.push(Value(DataType.UWORD, ubyte.integerValue()))
}
Opcode.MSB2WORD -> {
val byte = evalstack.pop()
@ -1336,30 +1468,38 @@ class StackVm(private var traceOutputFile: String?) {
evalstack.push(Value(DataType.UWORD, byte.integerValue() * 256))
}
Opcode.B2FLOAT -> {
val byte = evalstack.pop()
checkDt(byte, DataType.BYTE)
evalstack.push(Value(DataType.FLOAT, byte.integerValue()))
}
Opcode.UB2FLOAT -> {
val byte = evalstack.pop()
checkDt(byte, DataType.UBYTE)
evalstack.push(Value(DataType.FLOAT, byte.integerValue()))
}
Opcode.W2FLOAT -> {
val byte = evalstack.pop()
checkDt(byte, DataType.UWORD)
evalstack.push(Value(DataType.FLOAT, byte.integerValue()))
val wrd = evalstack.pop()
checkDt(wrd, DataType.UWORD)
evalstack.push(Value(DataType.FLOAT, wrd.integerValue()))
}
Opcode.LINE -> {
sourceLine = ins.callLabel!!
Opcode.UW2FLOAT -> {
val uwrd = evalstack.pop()
checkDt(uwrd, DataType.UWORD)
evalstack.push(Value(DataType.FLOAT, uwrd.integerValue()))
}
Opcode.READ_INDEXED_VAR_BYTE -> {
// put the byte value of variable[index] onto the stack
val index = evalstack.pop().integerValue()
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and get the byte value from that memory location
evalstack.push(Value(DataType.UBYTE, mem.getByte(variable.integerValue())))
// assume the variable is a pointer (address) and get the ubyte value from that memory location
evalstack.push(Value(DataType.UBYTE, mem.getUByte(variable.integerValue())))
} else {
// get indexed byte element from the array
val array = heap.get(variable.heapId)
when(array.type) {
DataType.ARRAY_UB, DataType.MATRIX_UB -> evalstack.push(Value(DataType.UBYTE, array.array!![index]))
DataType.ARRAY_B, DataType.MATRIX_B -> evalstack.push(Value(DataType.BYTE, array.array!![index]))
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> evalstack.push(Value(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0]))
else -> throw VmExecutionException("not a proper array/matrix/string variable with byte elements")
}
@ -1371,13 +1511,15 @@ class StackVm(private var traceOutputFile: String?) {
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and get the word value from that memory location
evalstack.push(Value(DataType.UWORD, mem.getWord(variable.integerValue())))
evalstack.push(Value(DataType.UWORD, mem.getUWord(variable.integerValue())))
} else {
// get indexed word element from the array
val array = heap.get(variable.heapId)
if(array.type!=DataType.ARRAY_UW)
throw VmExecutionException("not a proper array var with word elements")
evalstack.push(Value(DataType.UWORD, array.array!![index]))
when(array.type){
DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, array.array!![index]))
DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index]))
else -> throw VmExecutionException("not a proper array var with word elements")
}
}
}
Opcode.READ_INDEXED_VAR_FLOAT -> {
@ -1399,18 +1541,17 @@ class StackVm(private var traceOutputFile: String?) {
// store byte value on the stack in variable[index] (index is on the stack as well)
val index = evalstack.pop().integerValue()
val value = evalstack.pop()
checkDt(value, DataType.UBYTE)
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE))
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and write the byte value to that memory location
mem.setByte(variable.integerValue(), value.integerValue().toShort())
mem.setUByte(variable.integerValue(), value.integerValue().toShort())
} else {
// set indexed byte element in the array
val array = heap.get(variable.heapId)
when(array.type) {
DataType.ARRAY_UB, DataType.MATRIX_UB -> {
array.array!![index] = value.integerValue()
}
DataType.ARRAY_UB, DataType.MATRIX_UB -> array.array!![index] = value.integerValue()
DataType.ARRAY_B, DataType.MATRIX_B -> array.array!![index] = value.integerValue()
DataType.STR,
DataType.STR_P,
DataType.STR_S,
@ -1427,17 +1568,20 @@ class StackVm(private var traceOutputFile: String?) {
// store word value on the stack in variable[index] (index is on the stack as well)
val index = evalstack.pop().integerValue()
val value = evalstack.pop()
checkDt(value, DataType.UWORD)
checkDt(value, setOf(DataType.UWORD, DataType.WORD))
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and write the word value to that memory location
mem.setWord(variable.integerValue(), value.integerValue())
mem.setUWord(variable.integerValue(), value.integerValue())
} else {
// set indexed word element in the array
val array = heap.get(variable.heapId)
if(array.type!=DataType.ARRAY_UW)
throw VmExecutionException("not a proper array var with word elements")
array.array!![index] = value.integerValue()
when(array.type) {
DataType.ARRAY_UW -> array.array!![index] = value.integerValue()
DataType.ARRAY_W -> array.array!![index] = value.integerValue()
else -> throw VmExecutionException("not a proper array var with word elements")
}
}
}
Opcode.WRITE_INDEXED_VAR_FLOAT -> {
@ -1457,24 +1601,7 @@ class StackVm(private var traceOutputFile: String?) {
array.doubleArray!![index] = value.numericValue().toDouble()
}
}
Opcode.ADD_B -> TODO()
Opcode.ADD_W -> TODO()
Opcode.SUB_B -> TODO()
Opcode.SUB_W -> TODO()
Opcode.MUL_B -> TODO()
Opcode.MUL_W -> TODO()
Opcode.DIV_B -> TODO()
Opcode.DIV_W -> TODO()
Opcode.FLOORDIV_B -> TODO()
Opcode.FLOORDIV_W -> TODO()
Opcode.REMAINDER_B -> TODO()
Opcode.REMAINDER_W -> TODO()
Opcode.POW_B -> TODO()
Opcode.POW_W -> TODO()
Opcode.UB2UWORD -> TODO()
Opcode.UB2FLOAT -> TODO()
Opcode.UW2FLOAT -> TODO()
else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}")
//else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}")
}
if(traceOutput!=null) {
@ -1491,7 +1618,7 @@ class StackVm(private var traceOutputFile: String?) {
when (syscall) {
Syscall.WRITE_MEMCHR -> {
val address = evalstack.pop().integerValue()
print(Petscii.decodePetscii(listOf(mem.getByte(address)), true))
print(Petscii.decodePetscii(listOf(mem.getUByte(address)), true))
}
Syscall.WRITE_MEMSTR -> {
val address = evalstack.pop().integerValue()
@ -1550,7 +1677,7 @@ class StackVm(private var traceOutputFile: String?) {
Syscall.FUNC_LEN -> throw VmExecutionException("len() should have been const-folded away everywhere (it's not possible on non-const values)")
Syscall.FUNC_SIN -> evalstack.push(Value(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble())))
Syscall.FUNC_COS -> evalstack.push(Value(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble())))
Syscall.FUNC_ROUND -> evalstack.push(Value(DataType.UWORD, evalstack.pop().numericValue().toDouble().roundToInt()))
Syscall.FUNC_ROUND -> evalstack.push(Value(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt()))
Syscall.FUNC_ABS -> {
val value = evalstack.pop()
val absValue=
@ -1574,25 +1701,15 @@ class StackVm(private var traceOutputFile: String?) {
Syscall.FUNC_DEG -> evalstack.push(Value(DataType.FLOAT, Math.toDegrees(evalstack.pop().numericValue().toDouble())))
Syscall.FUNC_FLOOR -> {
val value = evalstack.pop()
val result =
when(value.type) {
DataType.UBYTE -> Value(DataType.UBYTE, value.numericValue())
DataType.UWORD -> Value(DataType.UWORD, value.numericValue())
DataType.FLOAT -> Value(DataType.UWORD, floor(value.numericValue().toDouble()))
else -> throw VmExecutionException("cannot get floor of $value")
}
evalstack.push(result)
if(value.type in NumericDatatypes)
evalstack.push(Value(DataType.WORD, floor(value.numericValue().toDouble()).toInt()))
else throw VmExecutionException("cannot get floor of $value")
}
Syscall.FUNC_CEIL -> {
val value = evalstack.pop()
val result =
when(value.type) {
DataType.UBYTE -> Value(DataType.UBYTE, value.numericValue())
DataType.UWORD -> Value(DataType.UWORD, value.numericValue())
DataType.FLOAT -> Value(DataType.UWORD, ceil(value.numericValue().toDouble()))
else -> throw VmExecutionException("cannot get ceil of $value")
}
evalstack.push(result)
if(value.type in NumericDatatypes)
evalstack.push(Value(DataType.WORD, ceil(value.numericValue().toDouble()).toInt()))
else throw VmExecutionException("cannot get ceil of $value")
}
Syscall.FUNC_MAX -> {
val iterable = evalstack.pop()
@ -1697,9 +1814,9 @@ class StackVm(private var traceOutputFile: String?) {
val jiffies = min((timestamp-bootTime)*60/1000, 24*3600*60-1)
// update the C-64 60hz jiffy clock in the ZP addresses:
mem.setByte(0x00a0, (jiffies ushr 16).toShort())
mem.setByte(0x00a1, (jiffies ushr 8 and 255).toShort())
mem.setByte(0x00a2, (jiffies and 255).toShort())
mem.setUByte(0x00a0, (jiffies ushr 16).toShort())
mem.setUByte(0x00a1, (jiffies ushr 8 and 255).toShort())
mem.setUByte(0x00a2, (jiffies and 255).toShort())
if(irqStartInstruction!=null) {
try {

View File

@ -18,19 +18,27 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
init {
when(type) {
DataType.UBYTE -> {
byteval = (numericvalueOrHeapId.toInt() and 255).toShort() // ubyte wrap around 0..255
if(numericvalueOrHeapId.toInt() !in 0..255)
throw VmExecutionException("value out of range: $numericvalueOrHeapId")
byteval = numericvalueOrHeapId.toShort()
asBooleanValue = byteval != (0.toShort())
}
DataType.BYTE -> {
byteval = limitByte(numericvalueOrHeapId.toInt())
if(numericvalueOrHeapId.toInt() !in -128..127)
throw VmExecutionException("value out of range: $numericvalueOrHeapId")
byteval = numericvalueOrHeapId.toShort()
asBooleanValue = byteval != (0.toShort())
}
DataType.UWORD -> {
wordval = numericvalueOrHeapId.toInt() and 65535 // uword wrap around 0..65535
if(numericvalueOrHeapId.toInt() !in 0..65535)
throw VmExecutionException("value out of range: $numericvalueOrHeapId")
wordval = numericvalueOrHeapId.toInt()
asBooleanValue = wordval != 0
}
DataType.WORD -> {
wordval = limitWord(numericvalueOrHeapId.toInt())
if(numericvalueOrHeapId.toInt() !in -32768..32767)
throw VmExecutionException("value out of range: $numericvalueOrHeapId")
wordval = numericvalueOrHeapId.toInt()
asBooleanValue = wordval != 0
}
DataType.FLOAT -> {
@ -46,35 +54,22 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
}
}
companion object {
fun limitByte(value: Int): Short {
var bval: Int
if(value < 0) {
bval = -(abs(value) and 127)
if(bval==0) bval=-128
}
else
bval = value and 127
return bval.toShort()
}
fun limitWord(value: Int): Int {
var bval: Int
if(value < 0) {
bval = -(abs(value) and 32767)
if(bval==0) bval=-32768
}
else
bval = value and 32767
return bval
}
}
override fun toString(): String {
return when(type) {
DataType.UBYTE -> "ub:%02x".format(byteval)
DataType.BYTE -> "b:%02x".format(byteval)
DataType.BYTE -> {
if(byteval!!<0)
"b:-%02x".format(abs(byteval!!.toInt()))
else
"b:%02x".format(byteval)
}
DataType.UWORD -> "uw:%04x".format(wordval)
DataType.WORD -> "w:%04x".format(wordval)
DataType.WORD -> {
if(wordval!!<0)
"w:-%04x".format(abs(wordval!!))
else
"w:%04x".format(wordval)
}
DataType.FLOAT -> "f:$floatval"
else -> "heap:$heapId"
}
@ -125,8 +120,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
if(result.toDouble() < 0 ) {
return when(leftDt) {
DataType.UBYTE, DataType.UWORD -> throw VmExecutionException("arithmetic error: cannot store a negative value in a $leftDt")
DataType.BYTE -> Value(DataType.BYTE, limitByte(result.toInt()))
DataType.WORD -> Value(DataType.WORD, limitWord(result.toInt()))
DataType.BYTE -> Value(DataType.BYTE, result.toInt())
DataType.WORD -> Value(DataType.WORD, result.toInt())
DataType.FLOAT -> Value(DataType.FLOAT, result)
else -> throw VmExecutionException("$op on non-numeric type")
}
@ -134,9 +129,9 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
return when(leftDt) {
DataType.UBYTE -> Value(DataType.UBYTE, result.toInt() and 255)
DataType.BYTE -> Value(DataType.BYTE, limitByte(result.toInt()))
DataType.BYTE -> Value(DataType.BYTE, result.toInt())
DataType.UWORD -> Value(DataType.UWORD, result.toInt() and 65535)
DataType.WORD -> Value(DataType.WORD, limitWord(result.toInt()))
DataType.WORD -> Value(DataType.WORD, result.toInt())
DataType.FLOAT -> Value(DataType.FLOAT, result)
else -> throw VmExecutionException("$op on non-numeric type")
}
@ -221,12 +216,20 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
fun shl(): Value {
val v = integerValue()
return Value(type, v shl 1)
if(type==DataType.UBYTE)
return Value(type, (v shl 1) and 255)
if(type==DataType.UWORD)
return Value(type, (v shl 1) and 65535)
throw VmExecutionException("invalid type for shl: $type")
}
fun shr(): Value {
val v = integerValue()
return Value(type, v ushr 1)
if(type==DataType.UBYTE)
return Value(type, (v ushr 1) and 255)
if(type==DataType.UWORD)
return Value(type, (v ushr 1) and 65535)
throw VmExecutionException("invalid type for shr: $type")
}
fun rol(carry: Boolean): Pair<Value, Boolean> {
@ -307,8 +310,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
fun neg(): Value {
return when(type) {
DataType.UBYTE -> Value(DataType.UBYTE, -(byteval!!))
DataType.UWORD -> Value(DataType.UWORD, -(wordval!!))
DataType.BYTE -> Value(DataType.BYTE, -(byteval!!))
DataType.WORD -> Value(DataType.WORD, -(wordval!!))
DataType.FLOAT -> Value(DataType.FLOAT, -(floatval)!!)
else -> throw VmExecutionException("neg can only work on byte/word/float")
}

View File

@ -141,27 +141,51 @@ class TestStackVmOpcodes {
@Test
fun testPushMem() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_MEM_BYTE, Value(DataType.UWORD, 0x2000)),
Instruction(Opcode.PUSH_MEM_WORD, Value(DataType.UWORD, 0x3000)),
Instruction(Opcode.PUSH_MEM_FLOAT, Value(DataType.UWORD, 0x4000))
Instruction(Opcode.PUSH_MEM_B, Value(DataType.UWORD, 0x2000)),
Instruction(Opcode.PUSH_MEM_UB, Value(DataType.UWORD, 0x3000)),
Instruction(Opcode.PUSH_MEM_W, Value(DataType.UWORD, 0x4000)),
Instruction(Opcode.PUSH_MEM_UW, Value(DataType.UWORD, 0x5000)),
Instruction(Opcode.PUSH_MEM_FLOAT, Value(DataType.UWORD, 0x6000))
)
val mem=mapOf(0x2000 to listOf(Value(DataType.UWORD, 0x42ea)),
0x3000 to listOf(Value(DataType.UWORD, 0x42ea)),
0x4000 to listOf(Value(DataType.FLOAT, 42.25)))
val mem=mapOf(0x2000 to listOf(Value(DataType.UWORD, 0xc2ca)),
0x3000 to listOf(Value(DataType.UWORD, 0xc2ca)),
0x4000 to listOf(Value(DataType.UWORD, 0xc2ca)),
0x5000 to listOf(Value(DataType.UWORD, 0xc2ca)),
0x6000 to listOf(Value(DataType.FLOAT, 42.25)))
vm.load(makeProg(ins, mem=mem), null)
assertEquals(0xea, vm.mem.getByte(0x2000))
assertEquals(0x42, vm.mem.getByte(0x2001))
assertEquals(0xea, vm.mem.getByte(0x3000))
assertEquals(0x42, vm.mem.getByte(0x3001))
assertEquals(0x42ea, vm.mem.getWord(0x2000))
assertEquals(0x42ea, vm.mem.getWord(0x3000))
assertEquals(42.25, vm.mem.getFloat(0x4000))
assertEquals(0xca, vm.mem.getUByte(0x2000))
assertEquals(0xc2, vm.mem.getUByte(0x2001))
assertEquals(0xca, vm.mem.getUByte(0x3000))
assertEquals(0xc2, vm.mem.getUByte(0x3001))
assertEquals(0xca, vm.mem.getUByte(0x4000))
assertEquals(0xc2, vm.mem.getUByte(0x4001))
assertEquals(0xca, vm.mem.getUByte(0x5000))
assertEquals(0xc2, vm.mem.getUByte(0x5001))
assertEquals(-54, vm.mem.getSByte(0x2000))
assertEquals(-62, vm.mem.getSByte(0x2001))
assertEquals(-54, vm.mem.getSByte(0x3000))
assertEquals(-62, vm.mem.getSByte(0x3001))
assertEquals(-54, vm.mem.getSByte(0x4000))
assertEquals(-62, vm.mem.getSByte(0x4001))
assertEquals(-54, vm.mem.getSByte(0x5000))
assertEquals(-62, vm.mem.getSByte(0x5001))
assertEquals(0xc2ca, vm.mem.getUWord(0x2000))
assertEquals(0xc2ca, vm.mem.getUWord(0x3000))
assertEquals(0xc2ca, vm.mem.getUWord(0x4000))
assertEquals(0xc2ca, vm.mem.getUWord(0x5000))
assertEquals(-15670, vm.mem.getSWord(0x2000))
assertEquals(-15670, vm.mem.getSWord(0x3000))
assertEquals(-15670, vm.mem.getSWord(0x4000))
assertEquals(-15670, vm.mem.getSWord(0x5000))
assertEquals(42.25, vm.mem.getFloat(0x6000))
assertThat(vm.evalstack, empty())
vm.step(3)
assertEquals(3, vm.evalstack.size)
vm.step(5)
assertEquals(5, vm.evalstack.size)
assertEquals(Value(DataType.FLOAT, 42.25), vm.evalstack.pop())
assertEquals(Value(DataType.UWORD, 0x42ea), vm.evalstack.pop())
assertEquals(Value(DataType.UBYTE, 0xea), vm.evalstack.pop())
assertEquals(Value(DataType.UWORD, 0xc2ca), vm.evalstack.pop())
assertEquals(Value(DataType.WORD, -15670), vm.evalstack.pop())
assertEquals(Value(DataType.UBYTE, 0xca), vm.evalstack.pop())
assertEquals(Value(DataType.BYTE, -54), vm.evalstack.pop())
}
@Test
@ -199,20 +223,32 @@ class TestStackVmOpcodes {
fun testPopMem() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, 42.25)),
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0x42ea)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 123)),
Instruction(Opcode.POP_MEM_BYTE, Value(DataType.UWORD, 0x2000)),
Instruction(Opcode.POP_MEM_WORD, Value(DataType.UWORD, 0x3000)),
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xc2ca)),
Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, -23456)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, -55)),
Instruction(Opcode.POP_MEM_B, Value(DataType.UWORD, 0x2000)),
Instruction(Opcode.POP_MEM_UB, Value(DataType.UWORD, 0x2001)),
Instruction(Opcode.POP_MEM_W, Value(DataType.UWORD, 0x3000)),
Instruction(Opcode.POP_MEM_UW, Value(DataType.UWORD, 0x3002)),
Instruction(Opcode.POP_MEM_FLOAT, Value(DataType.UWORD, 0x4000)))
vm.load(makeProg(ins), null)
assertEquals(0, vm.mem.getWord(0x2000))
assertEquals(0, vm.mem.getWord(0x3000))
assertEquals(0, vm.mem.getUWord(0x2000))
assertEquals(0, vm.mem.getUWord(0x2001))
assertEquals(0, vm.mem.getUWord(0x3000))
assertEquals(0, vm.mem.getUWord(0x3002))
assertEquals(0.0, vm.mem.getFloat(0x4000))
assertThat(vm.evalstack, empty())
vm.step(6)
vm.step(11)
assertThat(vm.evalstack, empty())
assertEquals(123, vm.mem.getByte(0x2000))
assertEquals(0x42ea, vm.mem.getWord(0x3000))
assertEquals(201, vm.mem.getUByte(0x2000))
assertEquals(177, vm.mem.getUByte(0x2001))
assertEquals(-55, vm.mem.getSByte(0x2000))
assertEquals(-79, vm.mem.getSByte(0x2001))
assertEquals(42080, vm.mem.getUWord(0x3000))
assertEquals(0xc2ca, vm.mem.getUWord(0x3002))
assertEquals(-23456, vm.mem.getSWord(0x3000))
assertEquals(-15670, vm.mem.getSWord(0x3002))
assertEquals(42.25, vm.mem.getFloat(0x4000))
}
@ -418,15 +454,27 @@ class TestStackVmOpcodes {
@Test
fun testNeg() {
testUnaryOperator(Value(DataType.UBYTE, 12), Opcode.NEG_B, Value(DataType.UBYTE, 244))
testUnaryOperator(Value(DataType.UWORD, 1234), Opcode.NEG_W, Value(DataType.UWORD, 64302))
testUnaryOperator(Value(DataType.BYTE, 12), Opcode.NEG_B, Value(DataType.BYTE, -12))
testUnaryOperator(Value(DataType.WORD, 1234), Opcode.NEG_W, Value(DataType.WORD, -1234))
testUnaryOperator(Value(DataType.FLOAT, 123.456), Opcode.NEG_F, Value(DataType.FLOAT, -123.456))
assertFailsWith<VmExecutionException> {
testUnaryOperator(Value(DataType.UBYTE, 12), Opcode.NEG_B, Value(DataType.UBYTE, 244))
}
assertFailsWith<VmExecutionException> {
testUnaryOperator(Value(DataType.UWORD, 1234), Opcode.NEG_W, Value(DataType.UWORD, 64302))
}
}
@Test
fun testInv() {
testUnaryOperator(Value(DataType.UBYTE, 123), Opcode.INV_BYTE, Value(DataType.UBYTE, 0x84))
testUnaryOperator(Value(DataType.UWORD, 4044), Opcode.INV_WORD, Value(DataType.UWORD, 0xf033))
assertFailsWith<VmExecutionException> {
testUnaryOperator(Value(DataType.BYTE, 123), Opcode.INV_BYTE, Value(DataType.BYTE, 0x84))
}
assertFailsWith<VmExecutionException> {
testUnaryOperator(Value(DataType.WORD, 4044), Opcode.INV_WORD, Value(DataType.WORD, 0xf033))
}
}
@Test
@ -464,13 +512,29 @@ class TestStackVmOpcodes {
@Test
fun testB2Word() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xea31)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 0x45)),
Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, 0x7a31)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, 127)),
Instruction(Opcode.B2WORD),
Instruction(Opcode.B2WORD)
)
vm.load(makeProg(ins), null)
vm.step(3)
assertEquals(Value(DataType.WORD, 127), vm.evalstack.pop())
assertFailsWith<VmExecutionException> {
vm.step(1)
}
}
@Test
fun testUB2Uword() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xea31)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 0x45)),
Instruction(Opcode.UB2UWORD),
Instruction(Opcode.UB2UWORD)
)
vm.load(makeProg(ins), null)
vm.step(3)
assertEquals(Value(DataType.UWORD, 0x0045), vm.evalstack.pop())
assertFailsWith<VmExecutionException> {
vm.step(1)
@ -496,14 +560,30 @@ class TestStackVmOpcodes {
@Test
fun testB2Float() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xea31)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 123)),
Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, 0x7a31)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, 127)),
Instruction(Opcode.B2FLOAT),
Instruction(Opcode.B2FLOAT)
)
vm.load(makeProg(ins), null)
vm.step(3)
assertEquals(Value(DataType.FLOAT, 123.0), vm.evalstack.pop())
assertEquals(Value(DataType.FLOAT, 127.0), vm.evalstack.pop())
assertFailsWith<VmExecutionException> {
vm.step(1)
}
}
@Test
fun testUB2Float() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xea31)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)),
Instruction(Opcode.UB2FLOAT),
Instruction(Opcode.UB2FLOAT)
)
vm.load(makeProg(ins), null)
vm.step(3)
assertEquals(Value(DataType.FLOAT, 177.0), vm.evalstack.pop())
assertFailsWith<VmExecutionException> {
vm.step(1)
}
@ -512,14 +592,30 @@ class TestStackVmOpcodes {
@Test
fun testW2Float() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 11)),
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 12345)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)),
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 52345)),
Instruction(Opcode.W2FLOAT),
Instruction(Opcode.W2FLOAT)
)
vm.load(makeProg(ins), null)
vm.step(3)
assertEquals(Value(DataType.FLOAT, 12345.0), vm.evalstack.pop())
assertEquals(Value(DataType.FLOAT, 52345.0), vm.evalstack.pop())
assertFailsWith<VmExecutionException> {
vm.step(1)
}
}
@Test
fun testUW2Float() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)),
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 52345)),
Instruction(Opcode.UW2FLOAT),
Instruction(Opcode.UW2FLOAT)
)
vm.load(makeProg(ins), null)
vm.step(3)
assertEquals(Value(DataType.FLOAT, 52345.0), vm.evalstack.pop())
assertFailsWith<VmExecutionException> {
vm.step(1)
}