This commit is contained in:
Irmen de Jong 2018-12-20 15:49:31 +01:00
parent e20e7f0232
commit 9ffc68acab
11 changed files with 216 additions and 147 deletions

View File

@ -11,7 +11,7 @@
; @todo docs: "integer / int will not result in float but is integer floor division." verify this!
sub toscreenx(float x, float z) -> word {
return 42 as word
return 42.w as word
}
asmsub blerp(ubyte x @ A, uword ding @ XY) -> clobbers() -> () {

View File

@ -2,8 +2,8 @@ package prog8.ast
import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.tree.TerminalNode
import prog8.compiler.CompilerException
import prog8.compiler.HeapValues
import prog8.compiler.intermediate.Value
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import prog8.functions.NotConstArgumentException
@ -990,65 +990,8 @@ class TypecastExpression(var expression: IExpression, var type: DataType, overri
override fun isIterable(namespace: INameScope, heap: HeapValues) = type in IterableDatatypes
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? {
val cv = expression.constValue(namespace, heap) ?: return null
return typecast(cv, type)
}
companion object {
fun typecast(cv: LiteralValue, type: DataType): LiteralValue? {
return when (cv.type) {
DataType.UBYTE -> {
when (type) {
DataType.UBYTE -> cv
DataType.BYTE -> TODO("ubyte->byte")
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = cv.asIntegerValue, position = cv.position)
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = cv.asIntegerValue, position = cv.position)
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = cv.asNumericValue!!.toDouble(), position = cv.position)
else -> throw CompilerException("invalid type cast from ${cv.type} to $type")
}
}
DataType.BYTE -> {
when (type) {
DataType.BYTE -> cv
DataType.UBYTE -> LiteralValue(DataType.UBYTE, (cv.asIntegerValue!! and 255).toShort(), position = cv.position)
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = cv.asIntegerValue!! and 65535, position = cv.position)
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = cv.asIntegerValue, position = cv.position)
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = cv.asNumericValue!!.toDouble(), position = cv.position)
else -> throw CompilerException("invalid type cast from ${cv.type} to $type")
}
}
DataType.UWORD -> {
when (type) {
DataType.BYTE -> TODO("uword->byte")
DataType.UBYTE -> LiteralValue(DataType.UBYTE, (cv.asIntegerValue!! and 255).toShort(), position = cv.position)
DataType.UWORD -> cv
DataType.WORD -> TODO("uword->word")
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = cv.asNumericValue!!.toDouble(), position = cv.position)
else -> throw CompilerException("invalid type cast from ${cv.type} to $type")
}
}
DataType.WORD -> {
when (type) {
DataType.BYTE -> TODO("word->byte")
DataType.UBYTE -> LiteralValue(DataType.UBYTE, (cv.asIntegerValue!! and 255).toShort(), position = cv.position)
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = cv.asIntegerValue!! and 65535, position = cv.position)
DataType.WORD -> cv
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = cv.asNumericValue!!.toDouble(), position = cv.position)
else -> throw CompilerException("invalid type cast from ${cv.type} to $type")
}
}
DataType.FLOAT -> {
when (type) {
DataType.BYTE -> TODO("float->byte")
DataType.UBYTE -> LiteralValue(DataType.UBYTE, (cv.floatvalue!!.toInt() and 255).toShort(), position = cv.position)
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = cv.floatvalue!!.toInt() and 65535, position = cv.position)
DataType.WORD -> TODO("float->word")
DataType.FLOAT -> cv
else -> throw CompilerException("invalid type cast from ${cv.type} to $type")
}
}
else -> throw CompilerException("invalid type cast from ${cv.type} to $type")
}
}
val value = Value(cv.type, cv.asNumericValue!!).cast(type)
return LiteralValue.fromNumber(value.numericValue(), value.type, position)
}
override fun toString(): String {

View File

@ -1975,14 +1975,16 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.UBYTE -> when(sourceDt) {
DataType.UBYTE -> {}
DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_UB)
DataType.UWORD, DataType.WORD -> prog.instr(Opcode.CAST_WRD_TO_UB)
DataType.UWORD-> prog.instr(Opcode.CAST_UW_TO_UB)
DataType.WORD-> prog.instr(Opcode.CAST_W_TO_UB)
DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_UB)
else -> throw CompilerException("invalid cast type $sourceDt")
}
DataType.BYTE -> when(sourceDt) {
DataType.UBYTE -> prog.instr(Opcode.CAST_UB_TO_B)
DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> prog.instr(Opcode.CAST_WRD_TO_B)
DataType.UWORD -> prog.instr(Opcode.CAST_UW_TO_B)
DataType.WORD -> prog.instr(Opcode.CAST_W_TO_B)
DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_B)
else -> throw CompilerException("invalid cast type $sourceDt")
}

View File

@ -192,8 +192,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
when (ins1.opcode) {
Opcode.CAST_B_TO_W, Opcode.CAST_B_TO_UW -> TODO("cast byte to (u)word")
Opcode.CAST_UB_TO_W, Opcode.CAST_UB_TO_UW -> TODO("cast ubyte to (u)word")
Opcode.CAST_WRD_TO_B -> TODO("cast (u)word to byte")
Opcode.CAST_WRD_TO_UB -> {
Opcode.CAST_W_TO_B, Opcode.CAST_UW_TO_B -> TODO("cast (u)word to byte")
Opcode.CAST_W_TO_UB, Opcode.CAST_UW_TO_UB -> {
val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, ins0.arg!!.integerValue() and 255))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
@ -208,6 +208,16 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_UW_TO_W -> {
val cv = ins0.arg!!.cast(DataType.WORD)
instructionsToReplace[index0] = Instruction(Opcode.PUSH_WORD, cv)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_W_TO_UW -> {
val cv = ins0.arg!!.cast(DataType.UWORD)
instructionsToReplace[index0] = Instruction(Opcode.PUSH_WORD, cv)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_WORD -> {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
@ -220,7 +230,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
fun optimizeByteConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
when (ins1.opcode) {
Opcode.CAST_B_TO_UB, Opcode.CAST_UB_TO_B,
Opcode.CAST_WRD_TO_B, Opcode.CAST_WRD_TO_UB -> instructionsToReplace[index1] = Instruction(Opcode.NOP)
Opcode.CAST_W_TO_B, Opcode.CAST_W_TO_UB,
Opcode.CAST_UW_TO_B, Opcode.CAST_UW_TO_UB -> instructionsToReplace[index1] = Instruction(Opcode.NOP)
Opcode.MSB -> throw CompilerException("msb of a byte")
Opcode.CAST_UB_TO_UW -> {
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, ins0.arg!!.integerValue()))
@ -261,10 +272,12 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
Opcode.CAST_B_TO_UW,
Opcode.CAST_B_TO_W,
Opcode.CAST_B_TO_F,
Opcode.CAST_UW_TO_UB,
Opcode.CAST_UW_TO_B,
Opcode.CAST_UW_TO_W,
Opcode.CAST_UW_TO_F,
Opcode.CAST_WRD_TO_UB,
Opcode.CAST_WRD_TO_B,
Opcode.CAST_W_TO_UB,
Opcode.CAST_W_TO_B,
Opcode.CAST_W_TO_UW,
Opcode.CAST_W_TO_F,
Opcode.CAST_F_TO_UB,

View File

@ -119,7 +119,7 @@ enum class Opcode {
INV_WORD,
// numeric type conversions
MSB, // note: lsb is equivalent to CAST_UW_TO_UB or CAST_WRD_TO_UB
MSB, // note: lsb is equivalent to CAST_UW_TO_UB or CAST_W_TO_UB
CAST_UB_TO_B,
CAST_UB_TO_UW,
CAST_UB_TO_W,
@ -128,10 +128,12 @@ enum class Opcode {
CAST_B_TO_UW,
CAST_B_TO_W,
CAST_B_TO_F,
CAST_WRD_TO_UB, // word and uword: just take the LSB
CAST_WRD_TO_B, // word and uword: just take the LSB
CAST_W_TO_UB,
CAST_W_TO_B,
CAST_W_TO_UW,
CAST_W_TO_F,
CAST_UW_TO_UB,
CAST_UW_TO_B,
CAST_UW_TO_W,
CAST_UW_TO_F,
CAST_F_TO_UB,

View File

@ -1,13 +1,15 @@
package prog8.compiler.intermediate
import prog8.ast.DataType
import prog8.ast.IterableDatatypes
import prog8.ast.NumericDatatypes
import prog8.stackvm.VmExecutionException
import prog8.ast.*
import java.lang.Exception
import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.pow
class ValueException(msg: String?) : Exception(msg)
class Value(val type: DataType, numericvalueOrHeapId: Number) {
private var byteval: Short? = null
private var wordval: Int? = null
@ -20,25 +22,25 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
when(type) {
DataType.UBYTE -> {
if(numericvalueOrHeapId.toInt() !in 0..255)
throw VmExecutionException("value out of range: $numericvalueOrHeapId")
throw ValueException("value out of range: $numericvalueOrHeapId")
byteval = numericvalueOrHeapId.toShort()
asBooleanValue = byteval != (0.toShort())
}
DataType.BYTE -> {
if(numericvalueOrHeapId.toInt() !in -128..127)
throw VmExecutionException("value out of range: $numericvalueOrHeapId")
throw ValueException("value out of range: $numericvalueOrHeapId")
byteval = numericvalueOrHeapId.toShort()
asBooleanValue = byteval != (0.toShort())
}
DataType.UWORD -> {
if(numericvalueOrHeapId.toInt() !in 0..65535)
throw VmExecutionException("value out of range: $numericvalueOrHeapId")
throw ValueException("value out of range: $numericvalueOrHeapId")
wordval = numericvalueOrHeapId.toInt()
asBooleanValue = wordval != 0
}
DataType.WORD -> {
if(numericvalueOrHeapId.toInt() !in -32768..32767)
throw VmExecutionException("value out of range: $numericvalueOrHeapId")
throw ValueException("value out of range: $numericvalueOrHeapId")
wordval = numericvalueOrHeapId.toInt()
asBooleanValue = wordval != 0
}
@ -48,7 +50,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
}
else -> {
if(numericvalueOrHeapId !is Int || numericvalueOrHeapId<0)
throw VmExecutionException("for non-numeric types, the value should be a integer heapId >= 0")
throw ValueException("for non-numeric types, the value should be a integer heapId >= 0")
heapId = numericvalueOrHeapId
asBooleanValue=true
}
@ -81,7 +83,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
DataType.UBYTE, DataType.BYTE -> byteval!!
DataType.UWORD, DataType.WORD -> wordval!!
DataType.FLOAT -> floatval!!
else -> throw VmExecutionException("invalid datatype for numeric value: $type")
else -> throw ValueException("invalid datatype for numeric value: $type")
}
}
@ -89,8 +91,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
return when(type) {
DataType.UBYTE, DataType.BYTE -> byteval!!.toInt()
DataType.UWORD, DataType.WORD -> wordval!!
DataType.FLOAT -> throw VmExecutionException("float to integer loss of precision")
else -> throw VmExecutionException("invalid datatype for integer value: $type")
DataType.FLOAT -> throw ValueException("float to integer loss of precision")
else -> throw ValueException("invalid datatype for integer value: $type")
}
}
@ -112,12 +114,12 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
operator fun compareTo(other: Value): Int {
return if (type in NumericDatatypes && other.type in NumericDatatypes)
numericValue().toDouble().compareTo(other.numericValue().toDouble())
else throw VmExecutionException("comparison can only be done between two numeric values")
else throw ValueException("comparison can only be done between two numeric values")
}
private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): Value {
if(leftDt!=rightDt)
throw VmExecutionException("left and right datatypes are not the same")
throw ValueException("left and right datatypes are not the same")
if(result.toDouble() < 0 ) {
return when(leftDt) {
DataType.UBYTE, DataType.UWORD -> {
@ -131,7 +133,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
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")
else -> throw ValueException("$op on non-numeric type")
}
}
@ -141,13 +143,13 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
DataType.UWORD -> Value(DataType.UWORD, result.toInt() and 65535)
DataType.WORD -> Value(DataType.WORD, result.toInt())
DataType.FLOAT -> Value(DataType.FLOAT, result)
else -> throw VmExecutionException("$op on non-numeric type")
else -> throw ValueException("$op on non-numeric type")
}
}
fun add(other: Value): Value {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
throw VmExecutionException("floating point loss of precision on type $type")
throw ValueException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
val result = v1.toDouble() + v2.toDouble()
@ -156,7 +158,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
fun sub(other: Value): Value {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
throw VmExecutionException("floating point loss of precision on type $type")
throw ValueException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
val result = v1.toDouble() - v2.toDouble()
@ -165,7 +167,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
fun mul(other: Value): Value {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
throw VmExecutionException("floating point loss of precision on type $type")
throw ValueException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
val result = v1.toDouble() * v2.toDouble()
@ -174,7 +176,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
fun div(other: Value): Value {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
throw VmExecutionException("floating point loss of precision on type $type")
throw ValueException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
if(v2.toDouble()==0.0) {
@ -189,13 +191,13 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
DataType.UBYTE -> Value(DataType.UBYTE, result)
DataType.UWORD -> Value(DataType.UWORD, result)
DataType.FLOAT -> Value(DataType.FLOAT, result)
else -> throw VmExecutionException("div on non-numeric type")
else -> throw ValueException("div on non-numeric type")
}
}
fun floordiv(other: Value): Value {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
throw VmExecutionException("floating point loss of precision on type $type")
throw ValueException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
val result = floor(v1.toDouble() / v2.toDouble())
@ -204,7 +206,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
DataType.UBYTE -> Value(DataType.UBYTE, result)
DataType.UWORD -> Value(DataType.UWORD, result)
DataType.FLOAT -> Value(DataType.FLOAT, result)
else -> throw VmExecutionException("div on non-numeric type")
else -> throw ValueException("div on non-numeric type")
}
}
@ -228,7 +230,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
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")
throw ValueException("invalid type for shl: $type")
}
fun shr(): Value {
@ -237,7 +239,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
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")
throw ValueException("invalid type for shr: $type")
}
fun rol(carry: Boolean): Pair<Value, Boolean> {
@ -255,7 +257,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
val newval = (v and 0x7fff shl 1) or (if(carry) 1 else 0)
Pair(Value(DataType.UWORD, newval), newCarry)
}
else -> throw VmExecutionException("rol can only work on byte/word")
else -> throw ValueException("rol can only work on byte/word")
}
}
@ -274,7 +276,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
val newval = (v ushr 1) or (if(carry) 0x8000 else 0)
Pair(Value(DataType.UWORD, newval), newCarry)
}
else -> throw VmExecutionException("ror2 can only work on byte/word")
else -> throw ValueException("ror2 can only work on byte/word")
}
}
@ -293,7 +295,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
val newval = (v and 0x7fff shl 1) or carry
Value(DataType.UWORD, newval)
}
else -> throw VmExecutionException("rol2 can only work on byte/word")
else -> throw ValueException("rol2 can only work on byte/word")
}
}
@ -312,7 +314,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
val newval = (v ushr 1) or carry
Value(DataType.UWORD, newval)
}
else -> throw VmExecutionException("ror2 can only work on byte/word")
else -> throw ValueException("ror2 can only work on byte/word")
}
}
@ -321,7 +323,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
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")
else -> throw ValueException("neg can only work on byte/word/float")
}
}
@ -330,7 +332,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
DataType.BYTE -> Value(DataType.BYTE, abs(byteval!!.toInt()))
DataType.WORD -> Value(DataType.WORD, abs(wordval!!))
DataType.FLOAT -> Value(DataType.FLOAT, abs(floatval!!))
else -> throw VmExecutionException("abs can only work on byte/word/float")
else -> throw ValueException("abs can only work on byte/word/float")
}
}
@ -364,7 +366,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
return when(type) {
DataType.UBYTE -> Value(DataType.UBYTE, byteval!!.toInt().inv() and 255)
DataType.UWORD -> Value(DataType.UWORD, wordval!!.inv() and 65535)
else -> throw VmExecutionException("inv can only work on byte/word")
else -> throw ValueException("inv can only work on byte/word")
}
}
@ -373,7 +375,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
DataType.UBYTE -> Value(DataType.UBYTE, (byteval!! + 1) and 255)
DataType.UWORD -> Value(DataType.UWORD, (wordval!! + 1) and 65535)
DataType.FLOAT -> Value(DataType.FLOAT, floatval!! + 1)
else -> throw VmExecutionException("inc can only work on byte/word/float")
else -> throw ValueException("inc can only work on byte/word/float")
}
}
@ -382,7 +384,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
DataType.UBYTE -> Value(DataType.UBYTE, (byteval!! - 1) and 255)
DataType.UWORD -> Value(DataType.UWORD, (wordval!! - 1) and 65535)
DataType.FLOAT -> Value(DataType.FLOAT, floatval!! - 1)
else -> throw VmExecutionException("dec can only work on byte/word/float")
else -> throw ValueException("dec can only work on byte/word/float")
}
}
@ -390,7 +392,84 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
return when(type) {
DataType.UBYTE, DataType.BYTE -> Value(DataType.UBYTE, 0)
DataType.UWORD, DataType.WORD -> Value(DataType.UBYTE, wordval!! ushr 8 and 255)
else -> throw VmExecutionException("msb can only work on (u)byte/(u)word")
else -> throw ValueException("msb can only work on (u)byte/(u)word")
}
}
fun cast(targetType: DataType): Value {
return when (type) {
DataType.UBYTE -> {
when (targetType) {
DataType.UBYTE -> this
DataType.BYTE -> {
if(byteval!!<=127)
Value(DataType.BYTE, byteval!!)
else
Value(DataType.BYTE, -(256-byteval!!))
}
DataType.UWORD -> Value(DataType.UWORD, numericValue())
DataType.WORD -> Value(DataType.WORD, numericValue())
DataType.FLOAT -> Value(DataType.FLOAT, numericValue())
else -> throw ValueException("invalid type cast from $type to $targetType")
}
}
DataType.BYTE -> {
when (targetType) {
DataType.BYTE -> this
DataType.UBYTE -> Value(DataType.UBYTE, integerValue() and 255)
DataType.UWORD -> Value(DataType.UWORD, integerValue() and 65535)
DataType.WORD -> Value(DataType.WORD, integerValue())
DataType.FLOAT -> Value(DataType.FLOAT, numericValue())
else -> throw ValueException("invalid type cast from $type to $targetType")
}
}
DataType.UWORD -> {
when (targetType) {
DataType.BYTE, DataType.UBYTE -> Value(DataType.UBYTE, integerValue() and 255)
DataType.UWORD -> this
DataType.WORD -> {
if(integerValue()<=32767)
Value(DataType.WORD, integerValue())
else
Value(DataType.WORD, -(65536-integerValue()))
}
DataType.FLOAT -> Value(DataType.FLOAT, numericValue())
else -> throw ValueException("invalid type cast from $type to $targetType")
}
}
DataType.WORD -> {
when (targetType) {
DataType.BYTE, DataType.UBYTE -> Value(DataType.UBYTE, integerValue() and 255)
DataType.UWORD -> Value(DataType.UWORD, integerValue() and 65535)
DataType.WORD -> this
DataType.FLOAT -> Value(DataType.FLOAT, numericValue())
else -> throw ValueException("invalid type cast from $type to $targetType")
}
}
DataType.FLOAT -> {
when (targetType) {
DataType.BYTE -> {
val integer=numericValue().toInt()
if(integer in -128..127)
Value(DataType.BYTE, integer)
else
throw AstException("overflow when casting float to byte: $this")
}
DataType.UBYTE -> Value(DataType.UBYTE, numericValue().toInt() and 255)
DataType.UWORD -> Value(DataType.UWORD, numericValue().toInt() and 65535)
DataType.WORD -> {
val integer=numericValue().toInt()
if(integer in -32768..32767)
Value(DataType.WORD, integer)
else
throw AstException("overflow when casting float to word: $this")
}
DataType.FLOAT -> this
else -> throw ValueException("invalid type cast from $type to $targetType")
}
}
else -> throw ValueException("invalid type cast from $type to $targetType")
}
}
}

View File

@ -674,20 +674,20 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.CAST_UB_TO_B -> "" // is a no-op, just carry on with the byte as-is
Opcode.CAST_W_TO_UW -> "" // is a no-op, just carry on with the word as-is
Opcode.CAST_UW_TO_W -> "" // is a no-op, just carry on with the word as-is
Opcode.CAST_WRD_TO_UB -> "" // is a no-op, just carry on with the lsb of the (u)word as-is
Opcode.CAST_WRD_TO_B -> "" // is a no-op, just carry on with the lsb of the (u)word as-is
Opcode.CAST_W_TO_UB -> "" // is a no-op, just carry on with the lsb of the word as-is
Opcode.CAST_W_TO_B -> "" // is a no-op, just carry on with the lsb of the word as-is
Opcode.CAST_UW_TO_UB -> "" // is a no-op, just carry on with the lsb of the uword as-is
Opcode.CAST_UW_TO_B -> "" // is a no-op, just carry on with the lsb of the uword as-is
Opcode.CAST_UB_TO_F -> " jsr prog8_lib.stack_ub2float"
Opcode.CAST_B_TO_F -> " jsr prog8_lib.stack_b2float"
Opcode.CAST_UW_TO_F -> " jsr prog8_lib.stack_uw2float"
Opcode.CAST_W_TO_F -> " jsr prog8_lib.stack_w2float"
Opcode.CAST_UB_TO_UW -> " lda #0 | sta ${ESTACK_HI+1},x" // clear the msb
Opcode.CAST_UB_TO_W -> TODO("ub2w")
Opcode.CAST_B_TO_UW -> TODO("b2uw")
Opcode.CAST_B_TO_W -> " ${signExtendA("${ESTACK_HI+1},x")}" // sign extend the lsb @todo missing an lda???
Opcode.CAST_F_TO_UB -> TODO("f2ub")
Opcode.CAST_F_TO_B -> TODO("f2b")
Opcode.CAST_F_TO_UW -> TODO("f2uw")
Opcode.CAST_F_TO_W -> TODO("f2w")
Opcode.CAST_F_TO_UB -> " jsr prog8_lib.stack_float2ub"
Opcode.CAST_F_TO_B -> " jsr prog8_lib.stack_float2b"
Opcode.CAST_F_TO_UW -> " jsr prog8_lib.stack_float2uw"
Opcode.CAST_F_TO_W -> " jsr prog8_lib.stack_float2w"
Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta ${ESTACK_HI+1},x" // clear the msb
Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " ${signExtendA("${ESTACK_HI+1},x")}" // sign extend the lsb @todo missing an lda???
Opcode.MSB -> " lda ${(ESTACK_HI+1).toHex()},x | sta ${(ESTACK_LO+1).toHex()},x"
Opcode.DIV_UB -> " jsr prog8_lib.div_ub"
@ -2715,8 +2715,18 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
// byte var = wordvar as (u)byte
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_WRD_TO_UB, Opcode.POP_VAR_BYTE),
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_WRD_TO_B, Opcode.POP_VAR_BYTE)) { segment ->
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_W_TO_UB, Opcode.POP_VAR_BYTE),
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_W_TO_B, Opcode.POP_VAR_BYTE)) { segment ->
when(segment[2].callLabel) {
"A" -> " lda ${segment[0].callLabel}"
"X" -> " ldx ${segment[0].callLabel}"
"Y" -> " ldy ${segment[0].callLabel}"
else -> " lda ${segment[0].callLabel} | sta ${segment[2].callLabel}"
}
},
// byte var = uwordvar as (u)byte
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_UW_TO_UB, Opcode.POP_VAR_BYTE),
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_UW_TO_B, Opcode.POP_VAR_BYTE)) { segment ->
when(segment[2].callLabel) {
"A" -> " lda ${segment[0].callLabel}"
"X" -> " ldx ${segment[0].callLabel}"
@ -2734,8 +2744,13 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
},
// push word var as (u)byte
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_WRD_TO_UB),
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_WRD_TO_B)) { segment ->
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_W_TO_UB),
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_W_TO_B)) { segment ->
" lda ${segment[0].callLabel} | sta ${ESTACK_LO.toHex()},x | dex "
},
// push uword var as (u)byte
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_UW_TO_UB),
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_UW_TO_B)) { segment ->
" lda ${segment[0].callLabel} | sta ${ESTACK_LO.toHex()},x | dex "
},
// push msb(word var)

View File

@ -54,7 +54,6 @@ val BuiltinFunctions = mapOf(
"clear_carry" to FunctionSignature(false, emptyList(), null),
"set_irqd" to FunctionSignature(false, emptyList(), null),
"clear_irqd" to FunctionSignature(false, emptyList(), null),
// @todo change the string conversion functions into "string as byte" type casts?
"str2byte" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.BYTE),
"str2ubyte" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.UBYTE),
"str2word" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.WORD),

View File

@ -1345,11 +1345,26 @@ class StackVm(private var traceOutputFile: String?) {
throw VmExecutionException("expected string to be on heap")
evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string
}
Opcode.CAST_UB_TO_B, Opcode.CAST_WRD_TO_B, Opcode.CAST_F_TO_B -> typecast(DataType.BYTE)
Opcode.CAST_B_TO_UB, Opcode.CAST_WRD_TO_UB, Opcode.CAST_F_TO_UB -> typecast(DataType.UBYTE)
Opcode.CAST_UB_TO_UW, Opcode.CAST_B_TO_UW, Opcode.CAST_W_TO_UW, Opcode.CAST_F_TO_UW -> typecast(DataType.UWORD)
Opcode.CAST_UB_TO_W, Opcode.CAST_B_TO_W, Opcode.CAST_UW_TO_W, Opcode.CAST_F_TO_W -> typecast(DataType.WORD)
Opcode.CAST_UB_TO_F, Opcode.CAST_B_TO_F, Opcode.CAST_UW_TO_F, Opcode.CAST_W_TO_F -> typecast(DataType.FLOAT)
Opcode.CAST_UB_TO_B -> typecast(DataType.UBYTE, DataType.BYTE)
Opcode.CAST_W_TO_B -> typecast(DataType.WORD, DataType.BYTE)
Opcode.CAST_UW_TO_B -> typecast(DataType.UWORD, DataType.BYTE)
Opcode.CAST_F_TO_B -> typecast(DataType.FLOAT, DataType.BYTE)
Opcode.CAST_B_TO_UB-> typecast(DataType.BYTE, DataType.UBYTE)
Opcode.CAST_W_TO_UB -> typecast(DataType.WORD, DataType.UBYTE)
Opcode.CAST_UW_TO_UB -> typecast(DataType.UWORD, DataType.UBYTE)
Opcode.CAST_F_TO_UB -> typecast(DataType.FLOAT, DataType.UBYTE)
Opcode.CAST_UB_TO_UW -> typecast(DataType.UBYTE, DataType.UWORD)
Opcode.CAST_B_TO_UW -> typecast(DataType.BYTE, DataType.UWORD)
Opcode.CAST_W_TO_UW -> typecast(DataType.WORD, DataType.UWORD)
Opcode.CAST_F_TO_UW -> typecast(DataType.FLOAT, DataType.UWORD)
Opcode.CAST_UB_TO_W -> typecast(DataType.UBYTE, DataType.WORD)
Opcode.CAST_B_TO_W -> typecast(DataType.BYTE, DataType.WORD)
Opcode.CAST_UW_TO_W -> typecast(DataType.UWORD, DataType.WORD)
Opcode.CAST_F_TO_W -> typecast(DataType.FLOAT, DataType.WORD)
Opcode.CAST_UB_TO_F -> typecast(DataType.UBYTE, DataType.FLOAT)
Opcode.CAST_B_TO_F -> typecast(DataType.BYTE, DataType.FLOAT)
Opcode.CAST_UW_TO_F -> typecast(DataType.UWORD, DataType.FLOAT)
Opcode.CAST_W_TO_F -> typecast(DataType.WORD, DataType.FLOAT)
//else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}")
}
@ -1362,11 +1377,11 @@ class StackVm(private var traceOutputFile: String?) {
return ins.next
}
private fun typecast(type: DataType) {
private fun typecast(from: DataType, to: DataType) {
val value = evalstack.pop()
val lv = LiteralValue.optimalNumeric(value.numericValue(), Position("?", 0, 0, 0))
val lv2 = TypecastExpression.typecast(lv, type) ?: throw VmExecutionException("type cast error")
evalstack.push(Value(lv2.type, lv2.asNumericValue!!))
checkDt(value, from)
val cv = value.cast(to)
evalstack.push(cv)
}
private fun dispatchSyscall(ins: Instruction) {

View File

@ -9,6 +9,7 @@ import prog8.compiler.HeapValues
import prog8.compiler.intermediate.Instruction
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
import prog8.compiler.intermediate.ValueException
import prog8.stackvm.*
import kotlin.test.*
@ -413,10 +414,10 @@ class TestStackVmOpcodes {
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> {
assertFailsWith<ValueException> {
testUnaryOperator(Value(DataType.BYTE, 123), Opcode.INV_BYTE, Value(DataType.BYTE, 0x84))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
testUnaryOperator(Value(DataType.WORD, 4044), Opcode.INV_WORD, Value(DataType.WORD, 0xf033))
}
}
@ -537,13 +538,13 @@ class TestStackVmOpcodes {
fun testW2Float() {
val ins = mutableListOf(
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)),
Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 52345)),
Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, -12345)),
Instruction(Opcode.CAST_W_TO_F),
Instruction(Opcode.CAST_W_TO_F)
)
vm.load(makeProg(ins), null)
vm.step(3)
assertEquals(Value(DataType.FLOAT, 52345.0), vm.evalstack.pop())
assertEquals(Value(DataType.FLOAT, -12345.0), vm.evalstack.pop())
assertFailsWith<VmExecutionException> {
vm.step(1)
}

View File

@ -3,10 +3,10 @@ package prog8tests
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.DataType
import prog8.ast.ExpressionError
import prog8.ast.LiteralValue
import prog8.ast.Position
import prog8.compiler.intermediate.Value
import prog8.compiler.intermediate.ValueException
import prog8.stackvm.VmExecutionException
import kotlin.test.*
@ -137,44 +137,44 @@ class TestStackVmValue {
@Test
fun testNoDtConversion() {
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UWORD, 100).add(Value(DataType.UBYTE, 120))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UBYTE, 100).add(Value(DataType.UWORD, 120))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.FLOAT, 100.22).add(Value(DataType.UWORD, 120))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UWORD, 1002).add(Value(DataType.FLOAT, 120.22))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.FLOAT, 100.22).add(Value(DataType.UBYTE, 120))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UBYTE, 12).add(Value(DataType.FLOAT, 120.22))
}
}
@Test
fun testNoAutoFloatConversion() {
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UBYTE, 233).add(Value(DataType.FLOAT, 1.234))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UWORD, 233).add(Value(DataType.FLOAT, 1.234))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UBYTE, 233).mul(Value(DataType.FLOAT, 1.234))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UWORD, 233).mul(Value(DataType.FLOAT, 1.234))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UBYTE, 233).div(Value(DataType.FLOAT, 1.234))
}
assertFailsWith<VmExecutionException> {
assertFailsWith<ValueException> {
Value(DataType.UWORD, 233).div(Value(DataType.FLOAT, 1.234))
}
val result = Value(DataType.FLOAT, 233.333).add(Value(DataType.FLOAT, 1.234))