vm: treat floats as 64 bits doubles. 0.0 printed as "0".

This commit is contained in:
Irmen de Jong
2023-11-20 23:19:08 +01:00
parent ab4bcdf12d
commit af5ca2d0b8
14 changed files with 139 additions and 98 deletions

View File

@@ -12,7 +12,7 @@ class VirtualMachineDefinition: IMachineDefinition {
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble() override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble() override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point override val FLOAT_MEM_SIZE = 8 // 64-bits double
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override val BSSHIGHRAM_START = 0u // not actually used override val BSSHIGHRAM_START = 0u // not actually used
@@ -22,8 +22,8 @@ class VirtualMachineDefinition: IMachineDefinition {
override fun getFloatAsmBytes(num: Number): String { override fun getFloatAsmBytes(num: Number): String {
// little endian binary representation // little endian binary representation
val bits = num.toFloat().toBits().toUInt() val bits = num.toDouble().toBits().toULong()
val hexStr = bits.toString(16).padStart(8, '0') val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { "\$" + it } val parts = hexStr.chunked(2).map { "\$" + it }
return parts.joinToString(", ") return parts.joinToString(", ")
} }

View File

@@ -33,7 +33,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
val resultFpRegister = codeGen.registers.nextFreeFloat() val resultFpRegister = codeGen.registers.nextFreeFloat()
code += IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, immediateFp = expr.number.toFloat()) code += IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, immediateFp = expr.number)
ExpressionCodeResult(code, vmDt,-1, resultFpRegister) ExpressionCodeResult(code, vmDt,-1, resultFpRegister)
} }
else { else {
@@ -713,7 +713,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) { if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
val factor = constFactorRight.number.toFloat() val factor = constFactorRight.number
result += codeGen.divideByConstFloat(tr.resultFpReg, factor) result += codeGen.divideByConstFloat(tr.resultFpReg, factor)
return ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg) return ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else { } else {
@@ -769,13 +769,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(constFactorLeft!=null) { return if(constFactorLeft!=null) {
val tr = translateExpression(binExpr.right) val tr = translateExpression(binExpr.right)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
val factor = constFactorLeft.number.toFloat() val factor = constFactorLeft.number
result += codeGen.multiplyByConstFloat(tr.resultFpReg, factor) result += codeGen.multiplyByConstFloat(tr.resultFpReg, factor)
ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg) ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else if(constFactorRight!=null) { } else if(constFactorRight!=null) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
val factor = constFactorRight.number.toFloat() val factor = constFactorRight.number
result += codeGen.multiplyByConstFloat(tr.resultFpReg, factor) result += codeGen.multiplyByConstFloat(tr.resultFpReg, factor)
ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg) ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else { } else {
@@ -823,7 +823,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.SUB, vmDt, fpReg1 = tr.resultFpReg, immediateFp = (binExpr.right as PtNumber).number.toFloat()), null) addInstr(result, IRInstruction(Opcode.SUB, vmDt, fpReg1 = tr.resultFpReg, immediateFp = (binExpr.right as PtNumber).number), null)
ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg) ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@@ -878,7 +878,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.ADD, vmDt, fpReg1 = tr.resultFpReg, immediateFp = (binExpr.right as PtNumber).number.toFloat()), null) addInstr(result, IRInstruction(Opcode.ADD, vmDt, fpReg1 = tr.resultFpReg, immediateFp = (binExpr.right as PtNumber).number), null)
ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg) ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@@ -950,7 +950,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val constFactorRight = operand as? PtNumber val constFactorRight = operand as? PtNumber
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) { if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toFloat() val factor = constFactorRight.number
result += codeGen.divideByConstFloatInplace(knownAddress, symbol, factor) result += codeGen.divideByConstFloatInplace(knownAddress, symbol, factor)
} else { } else {
val tr = translateExpression(operand) val tr = translateExpression(operand)
@@ -999,7 +999,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val constFactorRight = operand as? PtNumber val constFactorRight = operand as? PtNumber
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null) { if(constFactorRight!=null) {
val factor = constFactorRight.number.toFloat() val factor = constFactorRight.number
result += codeGen.multiplyByConstFloatInplace(knownAddress, symbol, factor) result += codeGen.multiplyByConstFloatInplace(knownAddress, symbol, factor)
} else { } else {
val tr = translateExpression(operand) val tr = translateExpression(operand)
@@ -1324,7 +1324,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat()) it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
@@ -1335,7 +1335,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat()) it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)

View File

@@ -710,23 +710,23 @@ class IRCodeGen(
return code return code
} }
internal fun multiplyByConstFloat(fpReg: Int, factor: Float): IRCodeChunk { internal fun multiplyByConstFloat(fpReg: Int, factor: Double): IRCodeChunk {
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(factor==1f) if(factor==1.0)
return code return code
code += if(factor==0f) { code += if(factor==0.0) {
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0f) IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
} else { } else {
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor) IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
} }
return code return code
} }
internal fun multiplyByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Float): IRCodeChunk { internal fun multiplyByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Double): IRCodeChunk {
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(factor==1f) if(factor==1.0)
return code return code
if(factor==0f) { if(factor==0.0) {
code += if(knownAddress!=null) code += if(knownAddress!=null)
IRInstruction(Opcode.STOREZM, IRDataType.FLOAT, address = knownAddress) IRInstruction(Opcode.STOREZM, IRDataType.FLOAT, address = knownAddress)
else else
@@ -807,25 +807,25 @@ class IRCodeGen(
return code return code
} }
internal fun divideByConstFloat(fpReg: Int, factor: Float): IRCodeChunk { internal fun divideByConstFloat(fpReg: Int, factor: Double): IRCodeChunk {
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(factor==1f) if(factor==1.0)
return code return code
code += if(factor==0f) { code += if(factor==0.0) {
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = Float.MAX_VALUE) IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = Double.MAX_VALUE)
} else { } else {
IRInstruction(Opcode.DIVS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor) IRInstruction(Opcode.DIVS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
} }
return code return code
} }
internal fun divideByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Float): IRCodeChunk { internal fun divideByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Double): IRCodeChunk {
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(factor==1f) if(factor==1.0)
return code return code
if(factor==0f) { if(factor==0.0) {
val maxvalueReg = registers.nextFreeFloat() val maxvalueReg = registers.nextFreeFloat()
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = maxvalueReg, immediateFp = Float.MAX_VALUE) code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = maxvalueReg, immediateFp = Double.MAX_VALUE)
code += if(knownAddress!=null) code += if(knownAddress!=null)
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, address = knownAddress) IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, address = knownAddress)
else else
@@ -1189,7 +1189,7 @@ class IRCodeGen(
addToResult(result, leftTr, -1, leftTr.resultFpReg) addToResult(result, leftTr, -1, leftTr.resultFpReg)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
val rightFpReg = registers.nextFreeFloat() val rightFpReg = registers.nextFreeFloat()
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = rightFpReg, immediateFp = 0f) it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = rightFpReg, immediateFp = 0.0)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightFpReg) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightFpReg)
} }
when (condition.operator) { when (condition.operator) {

View File

@@ -23,8 +23,7 @@ Future Things and Ideas
Compiler: Compiler:
- Currently "320*240/8/8" gives integer overflow, so: allow constant integer subexpressions to contain out of range integers (>65535 etc) as long as the final constant value is within byte/word range. - Currently "320*240/8/8" gives integer overflow, so: allow constant integer subexpressions to contain out of range integers (>65535 etc) as long as the final constant value is within byte/word range.
- allow 'chained' array indexing for expressions: value = ptrarray[0][0] - Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays.
- allow 'chained' array indexing for assign targets: ptrarray[0][0] = 42 this is just evaluating the lhs as a uword pointer expression
- fix the other cases of "TODO index could also be a binexpr" (in AssignmentAsmGen), but these are for float arrays so rarely used. - fix the other cases of "TODO index could also be a binexpr" (in AssignmentAsmGen), but these are for float arrays so rarely used.
- [much work:] more support for (64tass) SEGMENTS ? - [much work:] more support for (64tass) SEGMENTS ?
@@ -68,6 +67,7 @@ Libraries:
Optimizations: Optimizations:
- give a warning for variables that could be a const - or even make them a const (if not @shared)?
- treat every scalar variable decl with initialization value, as const by default, unless the variable gets assigned to somewhere (or has its address taken, or is @shared) - treat every scalar variable decl with initialization value, as const by default, unless the variable gets assigned to somewhere (or has its address taken, or is @shared)
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served? - VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest

View File

@@ -1,11 +1,14 @@
%import textio %import textio
%import floats
%zeropage basicsafe
main { main {
sub start() { sub start() {
str key = "test" float fl1 = 999.8877
txt.print(":") float fl2 = sqrt(fl1) * fl1 + fl1*33.44
if key != ":" { floats.print_f(fl2)
cx16.r0++ txt.nl()
} fl1=0
floats.print_f(fl1)
} }
} }

View File

@@ -11,7 +11,7 @@ Intermediate Representation instructions for the IR Virtual machine.
Specs of the virtual machine this will run on: Specs of the virtual machine this will run on:
Program to execute is not stored in the system memory, it's just a separate list of instructions. Program to execute is not stored in the system memory, it's just a separate list of instructions.
65536 virtual registers, 16 bits wide, can also be used as 8 bits. r0-r65535 65536 virtual registers, 16 bits wide, can also be used as 8 bits. r0-r65535
65536 virtual floating point registers (32 bits single precision floats) fr0-fr65535 65536 virtual floating point registers (64 bits double precision) fr0-fr65535
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits. 65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits.
Value stack, max 128 entries of 1 byte each. Value stack, max 128 entries of 1 byte each.
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!! Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!!
@@ -21,7 +21,7 @@ Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by t
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly. Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
Value types: integers (.b=byte=8 bits, .w=word=16 bits) and float (.f=32 bits). Omitting it defaults to b if the instruction requires a type. Value types: integers (.b=byte=8 bits, .w=word=16 bits) and float (.f=64 bits). Omitting it defaults to b if the instruction requires a type.
Currently ther is NO support for 24 or 32 bits integers. Currently ther is NO support for 24 or 32 bits integers.
There is no distinction between signed and unsigned integers. There is no distinction between signed and unsigned integers.
Instead, a different instruction is used if a distinction should be made (for example div and divs). Instead, a different instruction is used if a distinction should be made (for example div and divs).
@@ -691,7 +691,7 @@ data class IRInstruction(
val fpReg1: Int?=null, // 0-$ffff val fpReg1: Int?=null, // 0-$ffff
val fpReg2: Int?=null, // 0-$ffff val fpReg2: Int?=null, // 0-$ffff
val immediate: Int?=null, // 0-$ff or $ffff if word val immediate: Int?=null, // 0-$ff or $ffff if word
val immediateFp: Float?=null, val immediateFp: Double?=null,
val address: Int?=null, // 0-$ffff val address: Int?=null, // 0-$ffff
val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!) val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!)
var branchTarget: IRCodeChunkBase? = null, // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to. var branchTarget: IRCodeChunkBase? = null, // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to.

View File

@@ -62,15 +62,15 @@ fun convertIRType(typestr: String): IRDataType? {
} }
} }
fun parseIRValue(value: String): Float { fun parseIRValue(value: String): Double {
return if(value.startsWith("-")) return if(value.startsWith("-"))
-parseIRValue(value.substring(1)) -parseIRValue(value.substring(1))
else if(value.startsWith('$')) else if(value.startsWith('$'))
value.substring(1).toInt(16).toFloat() value.substring(1).toInt(16).toDouble()
else if(value.startsWith('%')) else if(value.startsWith('%'))
value.substring(1).toInt(2).toFloat() value.substring(1).toInt(2).toDouble()
else if(value.startsWith("0x")) else if(value.startsWith("0x"))
value.substring(2).toInt(16).toFloat() value.substring(2).toInt(16).toDouble()
else if(value.startsWith('_')) else if(value.startsWith('_'))
throw IRParseException("attempt to parse a label as numeric value") throw IRParseException("attempt to parse a label as numeric value")
else if(value.startsWith('&')) else if(value.startsWith('&'))
@@ -78,7 +78,7 @@ fun parseIRValue(value: String): Float {
else if(value.startsWith('@')) else if(value.startsWith('@'))
throw IRParseException("address-of @ should have been handled earlier") throw IRParseException("address-of @ should have been handled earlier")
else else
return value.toFloat() return value.toDouble()
} }
@@ -119,11 +119,11 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
var fpReg1: Int? = null var fpReg1: Int? = null
var fpReg2: Int? = null var fpReg2: Int? = null
var immediateInt: Int? = null var immediateInt: Int? = null
var immediateFp: Float? = null var immediateFp: Double? = null
var address: Int? = null var address: Int? = null
var labelSymbol: String? = null var labelSymbol: String? = null
fun parseValueOrPlaceholder(operand: String): Float? { fun parseValueOrPlaceholder(operand: String): Double? {
return if(operand[0].isLetter()) { return if(operand[0].isLetter()) {
null null
} else { } else {

View File

@@ -47,7 +47,7 @@ class Memory {
mem[address] = uv.toUByte() mem[address] = uv.toUByte()
} }
fun setFloat(address: Int, value: Float) { fun setFloat(address: Int, value: Double) {
var bits = value.toBits() var bits = value.toBits()
mem[address] = bits.toUByte() mem[address] = bits.toUByte()
bits = bits ushr 8 bits = bits ushr 8
@@ -56,11 +56,26 @@ class Memory {
mem[address+2] = bits.toUByte() mem[address+2] = bits.toUByte()
bits = bits ushr 8 bits = bits ushr 8
mem[address+3] = bits.toUByte() mem[address+3] = bits.toUByte()
bits = bits ushr 8
mem[address+4] = bits.toUByte()
bits = bits ushr 8
mem[address+5] = bits.toUByte()
bits = bits ushr 8
mem[address+6] = bits.toUByte()
bits = bits ushr 8
mem[address+7] = bits.toUByte()
} }
fun getFloat(address: Int): Float { fun getFloat(address: Int): Double {
val bits = mem[address] + 256u*mem[address+1] + 65536u*mem[address+2] + 16777216u*mem[address+3] val bits = mem[address].toLong() +
return Float.fromBits(bits.toInt()) (1L shl 8)*mem[address+1].toLong() +
(1L shl 16)*mem[address+2].toLong() +
(1L shl 24)*mem[address+3].toLong() +
(1L shl 32)*mem[address+4].toLong() +
(1L shl 40)*mem[address+5].toLong() +
(1L shl 48)*mem[address+6].toLong() +
(1L shl 56)*mem[address+7].toLong()
return Double.fromBits(bits)
} }
// for now, no LONG 32-bits and no FLOAT support. // for now, no LONG 32-bits and no FLOAT support.

View File

@@ -7,14 +7,14 @@ package prog8.vm
*/ */
class Registers { class Registers {
private val registers = Array<UShort>(65536) { 0u } private val registers = Array<UShort>(65536) { 0u }
private val floatRegisters = Array(65536) { 0f } private val floatRegisters = Array(65536) { 0.0 }
var cpuA: UByte = 0u var cpuA: UByte = 0u
var cpuX: UByte = 0u var cpuX: UByte = 0u
var cpuY: UByte = 0u var cpuY: UByte = 0u
fun reset() { fun reset() {
registers.fill(0u) registers.fill(0u)
floatRegisters.fill(0f) floatRegisters.fill(0.0)
cpuA = 0u cpuA = 0u
cpuX = 0u cpuX = 0u
cpuY = 0u cpuY = 0u
@@ -46,7 +46,7 @@ class Registers {
fun getFloat(reg:Int) = floatRegisters[reg] fun getFloat(reg:Int) = floatRegisters[reg]
fun setFloat(reg:Int, value: Float) { fun setFloat(reg:Int, value: Double) {
floatRegisters[reg] = value floatRegisters[reg] = value
} }
} }

View File

@@ -124,15 +124,16 @@ object SysCalls {
} }
private fun returnValue(returns: FunctionCallArgs.RegSpec, value: Comparable<Nothing>, vm: VirtualMachine) { private fun returnValue(returns: FunctionCallArgs.RegSpec, value: Comparable<Nothing>, vm: VirtualMachine) {
val vv: Float = when(value) { val vv: Double = when(value) {
is UByte -> value.toFloat() is UByte -> value.toDouble()
is UShort -> value.toFloat() is UShort -> value.toDouble()
is UInt -> value.toFloat() is UInt -> value.toDouble()
is Byte -> value.toFloat() is Byte -> value.toDouble()
is Short -> value.toFloat() is Short -> value.toDouble()
is Int -> value.toFloat() is Int -> value.toDouble()
is Float -> value is Float -> value.toDouble()
else -> (value as Number).toFloat() is Double -> value
else -> (value as Number).toDouble()
} }
when(returns.dt) { when(returns.dt) {
IRDataType.BYTE -> vm.registers.setUB(returns.registerNum, vv.toInt().toUByte()) IRDataType.BYTE -> vm.registers.setUB(returns.registerNum, vv.toInt().toUByte())
@@ -346,8 +347,11 @@ object SysCalls {
returnValue(callspec.returns!!, 0, vm) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.PRINT_F -> { Syscall.PRINT_F -> {
val value = getArgValues(callspec.arguments, vm).single() as Float val value = getArgValues(callspec.arguments, vm).single() as Double
print(value) if(value==0.0)
print("0")
else
print(value)
} }
Syscall.STR_TO_UWORD -> { Syscall.STR_TO_UWORD -> {
val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
@@ -373,7 +377,7 @@ object SysCalls {
Syscall.STR_TO_FLOAT -> { Syscall.STR_TO_FLOAT -> {
val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
val memstring = vm.memory.getString(stringAddr.toInt()) val memstring = vm.memory.getString(stringAddr.toInt())
returnValue(callspec.returns!!, memstring.toFloat(), vm) returnValue(callspec.returns!!, memstring.toDouble(), vm)
} }
Syscall.COMPARE_STRINGS -> { Syscall.COMPARE_STRINGS -> {
val (firstV, secondV) = getArgValues(callspec.arguments, vm) val (firstV, secondV) = getArgValues(callspec.arguments, vm)
@@ -390,7 +394,7 @@ object SysCalls {
returnValue(callspec.returns!!, 1, vm) returnValue(callspec.returns!!, 1, vm)
} }
Syscall.RNDFSEED -> { Syscall.RNDFSEED -> {
val seed = getArgValues(callspec.arguments, vm).single() as Float val seed = getArgValues(callspec.arguments, vm).single() as Double
if(seed>0) // always use negative seed, this mimics the behavior on CBM machines if(seed>0) // always use negative seed, this mimics the behavior on CBM machines
vm.randomSeedFloat(-seed) vm.randomSeedFloat(-seed)
else else
@@ -474,9 +478,9 @@ object SysCalls {
} }
Syscall.CLAMP_FLOAT -> { Syscall.CLAMP_FLOAT -> {
val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm) val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm)
val value = valueU as Float val value = valueU as Double
val minimum = minimumU as Float val minimum = minimumU as Double
val maximum = maximumU as Float val maximum = maximumU as Double
val result = min(max(value, minimum), maximum) val result = min(max(value, minimum), maximum)
returnValue(callspec.returns!!, result, vm) returnValue(callspec.returns!!, result, vm)
} }

View File

@@ -357,7 +357,7 @@ class VirtualMachine(irProgram: IRProgram) {
when(value.dt!!) { when(value.dt!!) {
IRDataType.BYTE -> valueStack.push(value.value as UByte) IRDataType.BYTE -> valueStack.push(value.value as UByte)
IRDataType.WORD -> valueStack.pushw(value.value as UShort) IRDataType.WORD -> valueStack.pushw(value.value as UShort)
IRDataType.FLOAT -> valueStack.pushf(value.value as Float) IRDataType.FLOAT -> valueStack.pushf(value.value as Double)
} }
value.dt=null value.dt=null
} }
@@ -531,7 +531,7 @@ class VirtualMachine(irProgram: IRProgram) {
when(i.type!!) { when(i.type!!) {
IRDataType.BYTE -> memory.setUB(i.address!!, 0u) IRDataType.BYTE -> memory.setUB(i.address!!, 0u)
IRDataType.WORD -> memory.setUW(i.address!!, 0u) IRDataType.WORD -> memory.setUW(i.address!!, 0u)
IRDataType.FLOAT -> memory.setFloat(i.address!!, 0f) IRDataType.FLOAT -> memory.setFloat(i.address!!, 0.0)
} }
nextPc() nextPc()
} }
@@ -540,7 +540,7 @@ class VirtualMachine(irProgram: IRProgram) {
when (i.type!!) { when (i.type!!) {
IRDataType.BYTE -> memory.setUB(registers.getUW(i.reg1!!).toInt(), 0u) IRDataType.BYTE -> memory.setUB(registers.getUW(i.reg1!!).toInt(), 0u)
IRDataType.WORD -> memory.setUW(registers.getUW(i.reg1!!).toInt(), 0u) IRDataType.WORD -> memory.setUW(registers.getUW(i.reg1!!).toInt(), 0u)
IRDataType.FLOAT -> memory.setFloat(registers.getUW(i.reg1!!).toInt(), 0f) IRDataType.FLOAT -> memory.setFloat(registers.getUW(i.reg1!!).toInt(), 0.0)
} }
nextPc() nextPc()
} }
@@ -549,7 +549,7 @@ class VirtualMachine(irProgram: IRProgram) {
when (i.type!!) { when (i.type!!) {
IRDataType.BYTE -> memory.setUB(i.address!! + registers.getUB(i.reg1!!).toInt(), 0u) IRDataType.BYTE -> memory.setUB(i.address!! + registers.getUB(i.reg1!!).toInt(), 0u)
IRDataType.WORD -> memory.setUW(i.address!! + registers.getUB(i.reg1!!).toInt(), 0u) IRDataType.WORD -> memory.setUW(i.address!! + registers.getUB(i.reg1!!).toInt(), 0u)
IRDataType.FLOAT -> memory.setFloat(i.address!! + registers.getUB(i.reg1!!).toInt(), 0f) IRDataType.FLOAT -> memory.setFloat(i.address!! + registers.getUB(i.reg1!!).toInt(), 0.0)
} }
nextPc() nextPc()
} }
@@ -1592,16 +1592,16 @@ class VirtualMachine(irProgram: IRProgram) {
memory.setSW(address, result.toShort()) memory.setSW(address, result.toShort())
} }
private fun arithFloat(left: Float, operator: String, right: Float): Float = when(operator) { private fun arithFloat(left: Double, operator: String, right: Double): Double = when(operator) {
"+" -> left + right "+" -> left + right
"-" -> left - right "-" -> left - right
"*" -> left * right "*" -> left * right
"/" -> { "/" -> {
if(right==0f) Float.MAX_VALUE if(right==0.0) Double.MAX_VALUE
else left / right else left / right
} }
"%" -> { "%" -> {
if(right==0f) Float.MAX_VALUE if(right==0.0) Double.MAX_VALUE
else left % right else left % right
} }
else -> throw IllegalArgumentException("operator word $operator") else -> throw IllegalArgumentException("operator word $operator")
@@ -2108,22 +2108,22 @@ class VirtualMachine(irProgram: IRProgram) {
} }
private fun InsFFROMUB(i: IRInstruction) { private fun InsFFROMUB(i: IRInstruction) {
registers.setFloat(i.fpReg1!!, registers.getUB(i.reg1!!).toFloat()) registers.setFloat(i.fpReg1!!, registers.getUB(i.reg1!!).toDouble())
nextPc() nextPc()
} }
private fun InsFFROMSB(i: IRInstruction) { private fun InsFFROMSB(i: IRInstruction) {
registers.setFloat(i.fpReg1!!, registers.getSB(i.reg1!!).toFloat()) registers.setFloat(i.fpReg1!!, registers.getSB(i.reg1!!).toDouble())
nextPc() nextPc()
} }
private fun InsFFROMUW(i: IRInstruction) { private fun InsFFROMUW(i: IRInstruction) {
registers.setFloat(i.fpReg1!!, registers.getUW(i.reg1!!).toFloat()) registers.setFloat(i.fpReg1!!, registers.getUW(i.reg1!!).toDouble())
nextPc() nextPc()
} }
private fun InsFFROMSW(i: IRInstruction) { private fun InsFFROMSW(i: IRInstruction) {
registers.setFloat(i.fpReg1!!, registers.getSW(i.reg1!!).toFloat()) registers.setFloat(i.fpReg1!!, registers.getSW(i.reg1!!).toDouble())
nextPc() nextPc()
} }
@@ -2356,7 +2356,7 @@ class VirtualMachine(irProgram: IRProgram) {
randomGenerator = Random(((seed1.toUInt() shl 16) or seed2.toUInt()).toInt()) randomGenerator = Random(((seed1.toUInt() shl 16) or seed2.toUInt()).toInt())
} }
fun randomSeedFloat(seed: Float) { fun randomSeedFloat(seed: Double) {
randomGeneratorFloats = Random(seed.toBits()) randomGeneratorFloats = Random(seed.toBits())
} }
} }
@@ -2366,7 +2366,7 @@ internal fun Stack<UByte>.pushw(value: UShort) {
push((value.toInt() ushr 8).toUByte()) push((value.toInt() ushr 8).toUByte())
} }
internal fun Stack<UByte>.pushf(value: Float) { internal fun Stack<UByte>.pushf(value: Double) {
// push float; lsb first, msb last // push float; lsb first, msb last
var bits = value.toBits() var bits = value.toBits()
push(bits.toUByte()) push(bits.toUByte())
@@ -2376,6 +2376,14 @@ internal fun Stack<UByte>.pushf(value: Float) {
push(bits.toUByte()) push(bits.toUByte())
bits = bits ushr 8 bits = bits ushr 8
push(bits.toUByte()) push(bits.toUByte())
bits = bits ushr 8
push(bits.toUByte())
bits = bits ushr 8
push(bits.toUByte())
bits = bits ushr 8
push(bits.toUByte())
bits = bits ushr 8
push(bits.toUByte())
} }
internal fun Stack<UByte>.popw(): UShort { internal fun Stack<UByte>.popw(): UShort {
@@ -2384,14 +2392,25 @@ internal fun Stack<UByte>.popw(): UShort {
return ((msb.toInt() shl 8) + lsb.toInt()).toUShort() return ((msb.toInt() shl 8) + lsb.toInt()).toUShort()
} }
internal fun Stack<UByte>.popf(): Float { internal fun Stack<UByte>.popf(): Double {
// pop float; lsb is on bottom, msb on top // pop float; lsb is on bottom, msb on top
val b0 = pop() val b0 = pop().toLong()
val b1 = pop() val b1 = pop().toLong()
val b2 = pop() val b2 = pop().toLong()
val b3 = pop() val b3 = pop().toLong()
val bits = b3 + 256u*b2 + 65536u*b1 + 16777216u*b0 val b4 = pop().toLong()
return Float.fromBits(bits.toInt()) val b5 = pop().toLong()
val b6 = pop().toLong()
val b7 = pop().toLong()
val bits = b7 +
(1L shl 8)*b6 +
(1L shl 16)*b5 +
(1L shl 24)*b4
(1L shl 32)*b3
(1L shl 40)*b2
(1L shl 48)*b1
(1L shl 56)*b0
return Double.fromBits(bits)
} }
// probably called via reflection // probably called via reflection

View File

@@ -217,7 +217,7 @@ class VmProgramLoader {
addr += 2 addr += 2
} }
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
memory.setFloat(addr, 0.0f) memory.setFloat(addr, 0.0)
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
} }
in SplitWordArrayTypes -> { in SplitWordArrayTypes -> {
@@ -238,7 +238,7 @@ class VmProgramLoader {
DataType.BYTE -> memory.setSB(addr, it.toInt().toByte()) DataType.BYTE -> memory.setSB(addr, it.toInt().toByte())
DataType.UWORD -> memory.setUW(addr, it.toInt().toUShort()) DataType.UWORD -> memory.setUW(addr, it.toInt().toUShort())
DataType.WORD -> memory.setSW(addr, it.toInt().toShort()) DataType.WORD -> memory.setSW(addr, it.toInt().toShort())
DataType.FLOAT -> memory.setFloat(addr, it.toFloat()) DataType.FLOAT -> memory.setFloat(addr, it)
else -> throw IRParseException("invalid dt") else -> throw IRParseException("invalid dt")
} }
} }
@@ -314,7 +314,7 @@ class VmProgramLoader {
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
for (elt in iElts) { for (elt in iElts) {
val value = getInitializerValue(variable.dt, elt, symbolAddresses).toFloat() val value = getInitializerValue(variable.dt, elt, symbolAddresses).toDouble()
memory.setFloat(address, value) memory.setFloat(address, value)
address += program.options.compTarget.machine.FLOAT_MEM_SIZE address += program.options.compTarget.machine.FLOAT_MEM_SIZE
} }
@@ -378,7 +378,7 @@ class VmProgramLoader {
} }
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
val value = getInitializerValue(variable.dt, iElt, symbolAddresses).toFloat() val value = getInitializerValue(variable.dt, iElt, symbolAddresses).toDouble()
repeat(variable.length!!) { repeat(variable.length!!) {
memory.setFloat(address, value) memory.setFloat(address, value)
address += program.options.compTarget.machine.FLOAT_MEM_SIZE address += program.options.compTarget.machine.FLOAT_MEM_SIZE

View File

@@ -48,10 +48,10 @@ class TestMemory: FunSpec({
test("32 bits float access") { test("32 bits float access") {
val mem = Memory() val mem = Memory()
mem.getFloat(1000) shouldNotBe 0.0 mem.getFloat(1000) shouldNotBe 0.0
mem.setFloat(1000, 0.0f) mem.setFloat(1000, 0.0)
mem.getFloat(1000) shouldBe 0.0f mem.getFloat(1000) shouldBe 0.0
mem.setFloat(1000, -9.876543f) mem.setFloat(1000, -9.876543)
mem.getFloat(1000) shouldBe -9.876543f mem.getFloat(1000) shouldBe -9.876543
} }
test("setstring and getstring") { test("setstring and getstring") {

View File

@@ -128,6 +128,6 @@ class TestVm: FunSpec( {
c64machine.getFloatAsmBytes(Math.PI) shouldBe "\$82, \$49, \$0f, \$da, \$a2" c64machine.getFloatAsmBytes(Math.PI) shouldBe "\$82, \$49, \$0f, \$da, \$a2"
val vm = VirtualMachineDefinition() val vm = VirtualMachineDefinition()
vm.getFloatAsmBytes(Math.PI) shouldBe "\$40, \$49, \$0f, \$db" vm.getFloatAsmBytes(Math.PI) shouldBe "\$40, \$09, \$21, \$fb, \$54, \$44, \$2d, \$18"
} }
}) })