vm: and/or/xor/not are all bitwise operations again

This commit is contained in:
Irmen de Jong 2022-06-28 02:38:19 +02:00
parent ef92451d1a
commit 435d6f6f3f
12 changed files with 127 additions and 120 deletions

View File

@ -22,9 +22,9 @@ class ConstExprEvaluator {
"&" -> bitwiseand(left, right)
"|" -> bitwiseor(left, right)
"^" -> bitwisexor(left, right)
"and" -> logicaland(left, right)
"or" -> logicalor(left, right)
"xor" -> logicalxor(left, right)
"and" -> logicaland(left, right) // TODO bitwise and?
"or" -> logicalor(left, right) // TODO bitwise or?
"xor" -> logicalxor(left, right) // TODO bitwise xor?
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
">" -> NumericLiteral.fromBoolean(left > right, left.position)
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
@ -59,7 +59,7 @@ class ConstExprEvaluator {
}
private fun logicalxor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-bitxor $right"
val error = "cannot compute $left logical-xor $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
@ -76,7 +76,7 @@ class ConstExprEvaluator {
}
private fun logicalor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-or $right"
val error = "cannot compute $left logical-or $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
@ -93,7 +93,7 @@ class ConstExprEvaluator {
}
private fun logicaland(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-and $right"
val error = "cannot compute $left logical-and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)

View File

@ -12,10 +12,7 @@ import prog8.ast.statements.IfElse
import prog8.ast.statements.Jump
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType
import prog8.code.core.IntegerDatatypes
import prog8.code.core.NumericDatatypes
import prog8.code.core.*
import kotlin.math.abs
import kotlin.math.log2
import kotlin.math.pow
@ -220,8 +217,8 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
when {
leftVal != null && !leftVal.asBooleanValue -> expr.right
rightVal != null && !rightVal.asBooleanValue -> expr.left
leftVal != null && leftVal.asBooleanValue -> PrefixExpression("not", expr.right, expr.right.position)
rightVal != null && rightVal.asBooleanValue -> PrefixExpression("not", expr.left, expr.left.position)
leftVal != null && leftVal.asBooleanValue -> BinaryExpression(expr.right, "==", NumericLiteral.optimalInteger(0, Position.DUMMY), expr.right.position)
rightVal != null && rightVal.asBooleanValue -> BinaryExpression(expr.left, "==", NumericLiteral.optimalInteger(0, Position.DUMMY), expr.left.position)
else -> null
}
}

View File

@ -104,22 +104,28 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
// so a lot of useless checks and conversions are added. Here we can be smarter so the codegen
// can just rely on the correct value of the operands (0 or 1) if they're boolean, and just use bitwise instructions.
if(expr.operator in LogicalOperators) {
fun wrapped(expr: Expression): Expression {
return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
expr
else if(expr is BinaryExpression && expr.operator in LogicalOperators+ComparisonOperators)
expr
else
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
}
return listOf(
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
IAstModification.ReplaceNodeSafe(expr.left, wrapWithBooleanConversion(expr.left), expr),
IAstModification.ReplaceNodeSafe(expr.right, wrapWithBooleanConversion(expr.right), expr)
)
}
return noModifications
}
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator == "not") {
// To enable simple bitwise and/or/xor/not instructions in the codegen for the logical and/or/xor/not,
// we wrap the operands in a call to boolean() if required so that they are 0 or 1 as needed.
// Making the codegen more generic to do this by itself all the time will generate much larger
// code because it is hard to decide there if the value conversion to 0 or 1 is needed or not,
// so a lot of useless checks and conversions are added. Here we can be smarter so the codegen
// can just rely on the correct value of the operands (0 or 1) if they're boolean, and just use bitwise instructions.
// not(x) --> boolean(x)==0
val replacement = BinaryExpression(wrapWithBooleanConversion(expr.expression), "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent))
}
return noModifications
}
@ -156,4 +162,13 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
return noModifications
}
private fun wrapWithBooleanConversion(expr: Expression): Expression {
return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
expr
else if(expr is BinaryExpression && expr.operator in LogicalOperators+ComparisonOperators)
expr
else
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
}
}

View File

@ -107,15 +107,10 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val logical = parent as? BinaryExpression
if(logical!=null && logical.operator in LogicalOperators) {
// not x as operand in a logical expression --> cast it to ubyte
// TODO is this cast really necessary or does boolean() wrapping take care of it?
val cast = TypecastExpression(expr, DataType.UBYTE, true, expr.position)
return listOf(IAstModification.ReplaceNode(expr, cast, parent))
}
val boolCast = parent as? IFunctionCall
if(boolCast!=null && boolCast.target.nameInSource==listOf("boolean")) {
// boolean(not x) --> (not x) as ubyte
val cast = TypecastExpression(expr, DataType.UBYTE, true, expr.position)
return listOf(IAstModification.ReplaceNode(boolCast as Node, cast, boolCast.parent))
}
}
return noModifications
}

View File

@ -12,7 +12,6 @@ import prog8.ast.statements.FunctionCallStatement
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.code.target.VMTarget
internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() {
@ -250,20 +249,6 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
if(fcall!=null && fcall.target.nameInSource==listOf("boolean")) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, fcall as Node, parent))
}
if(options.compTarget.name == VMTarget.NAME) {
// if target is Virtual, remove ALL boolean() conversions of operands to logical expressions
// we can do this because the logical and/or/xor/not in the virtual machine behave correctly
// with operand values other than 0 and 1 (0 = false, everything else=true, result is always 0 or 1)
val logicalExpr = functionCallExpr.parent as? BinaryExpression
if(logicalExpr!=null && logicalExpr.operator in LogicalOperators + ComparisonOperators) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args.single(), parent))
}
val prefixExpr = functionCallExpr.parent as? PrefixExpression
if(prefixExpr!=null && prefixExpr.operator in LogicalOperators) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args.single(), parent))
}
}
}
return noModifications
}

View File

@ -285,12 +285,12 @@ class TestOptimization: FunSpec({
"""
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
// bb = (not bb or (not ww as ubyte) )
// bb = ((boolean(bb)==0) or (boolean(ww)==0))
val bbAssign = result.program.entrypoint.statements.last() as Assignment
val expr = bbAssign.value as BinaryExpression
expr.operator shouldBe "or"
expr.left shouldBe instanceOf<PrefixExpression>()
expr.right shouldBe instanceOf<TypecastExpression>() // not of word typecast into ubyte
expr.left shouldBe instanceOf<BinaryExpression>()
expr.right shouldBe instanceOf<BinaryExpression>()
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
@ -303,33 +303,33 @@ class TestOptimization: FunSpec({
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
printProgram(result.program)
// TODO this is no longer the case:
// assignment is now split into:
// bb = not bb
// bb = (bb or (not ww as ubyte)
val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>()
val bbAssigns = assigns.filter { it.value !is NumericLiteral }
bbAssigns.size shouldBe 2
bbAssigns[0].target.identifier!!.nameInSource shouldBe listOf("bb")
bbAssigns[0].value shouldBe instanceOf<PrefixExpression>()
(bbAssigns[0].value as PrefixExpression).operator shouldBe "not"
((bbAssigns[0].value as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
bbAssigns[0].value.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
bbAssigns[1].target.identifier!!.nameInSource shouldBe listOf("bb")
val bbAssigns1expr = bbAssigns[1].value as BinaryExpression
bbAssigns1expr.operator shouldBe "or"
(bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
bbAssigns1expr.right shouldBe instanceOf<TypecastExpression>()
val castedValue = (bbAssigns1expr.right as TypecastExpression).expression as PrefixExpression
castedValue.operator shouldBe "not"
(castedValue.expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
val asm = generateAssembly(result.program, options)
asm shouldNotBe null
asm!!.name.shouldNotBeBlank()
// val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>()
// val bbAssigns = assigns.filter { it.value !is NumericLiteral }
// bbAssigns.size shouldBe 2
//
// bbAssigns[0].target.identifier!!.nameInSource shouldBe listOf("bb")
// bbAssigns[0].value shouldBe instanceOf<PrefixExpression>()
// (bbAssigns[0].value as PrefixExpression).operator shouldBe "not"
// ((bbAssigns[0].value as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
// bbAssigns[0].value.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
//
// bbAssigns[1].target.identifier!!.nameInSource shouldBe listOf("bb")
// val bbAssigns1expr = bbAssigns[1].value as BinaryExpression
// bbAssigns1expr.operator shouldBe "or"
// (bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
// bbAssigns1expr.right shouldBe instanceOf<TypecastExpression>()
// val castedValue = (bbAssigns1expr.right as TypecastExpression).expression as PrefixExpression
// castedValue.operator shouldBe "not"
// (castedValue.expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
// bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
//
// val asm = generateAssembly(result.program, options)
// asm shouldNotBe null
// asm!!.name.shouldNotBeBlank()
}
test("intermediate assignment steps generated for typecasted expression") {
@ -455,13 +455,15 @@ class TestOptimization: FunSpec({
}
}"""
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
printProgram(result.program)
/* expected:
ubyte z1
z1 = 10
ubyte z2
z2 = 255
ubyte z3
z3 = 1
z3 = 0
z3 = (boolean(z3)==0)
uword z4
z4 = 0
ubyte z5
@ -472,28 +474,30 @@ class TestOptimization: FunSpec({
z6 -= 5
*/
val statements = result.program.entrypoint.statements
statements.size shouldBe 14
statements.size shouldBe 15
val z1decl = statements[0] as VarDecl
val z1init = statements[1] as Assignment
val z2decl = statements[2] as VarDecl
val z2init = statements[3] as Assignment
val z3decl = statements[4] as VarDecl
val z3init = statements[5] as Assignment
val z4decl = statements[6] as VarDecl
val z4init = statements[7] as Assignment
val z5decl = statements[8] as VarDecl
val z5init = statements[9] as Assignment
val z5plus = statements[10] as Assignment
val z6decl = statements[11] as VarDecl
val z6init = statements[12] as Assignment
val z6plus = statements[13] as Assignment
val z3not = statements[6] as Assignment
val z4decl = statements[7] as VarDecl
val z4init = statements[8] as Assignment
val z5decl = statements[9] as VarDecl
val z5init = statements[10] as Assignment
val z5plus = statements[11] as Assignment
val z6decl = statements[12] as VarDecl
val z6init = statements[13] as Assignment
val z6plus = statements[14] as Assignment
z1decl.name shouldBe "z1"
z1init.value shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
z2decl.name shouldBe "z2"
z2init.value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
z3decl.name shouldBe "z3"
z3init.value shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
z3init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
z3not.value shouldBe instanceOf<BinaryExpression>()
z4decl.name shouldBe "z4"
z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
z5decl.name shouldBe "z5"

View File

@ -152,7 +152,6 @@ class TestTypecasts: FunSpec({
}
"""
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
printProgram(result.program)
val statements = result.program.entrypoint.statements
statements.size shouldBe 27
}
@ -181,7 +180,7 @@ class TestTypecasts: FunSpec({
}"""
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
val statements = result.program.entrypoint.statements
statements.size shouldBe 14
statements.size shouldBe 13
}
test("no infinite typecast loop in assignment asmgen") {

View File

@ -62,6 +62,7 @@ main {
else
txt.print(" / fail")
txt.nl()
bvalue = bvalue and 128
txt.print("bvl 1: ")
txt.print_ub(bvalue)
@ -121,9 +122,9 @@ main {
txt.print(" / fail")
txt.nl()
txt.print(" or 1: ")
bvalue = ub4 or ub4 or 64
bvalue = ub4 or ub4 or ub1
txt.print_ub(bvalue)
if ub4 or ub4 or 64
if ub4 or ub4 or ub1
txt.print(" / ok")
else
txt.print(" / fail")
@ -169,6 +170,5 @@ main {
txt.print(" / fail")
else
txt.print(" / ok")
txt.nl()
}
}

View File

@ -65,6 +65,18 @@ interface IAstModification {
}
}
class ReplaceNodeSafe(val node: Node, private val replacement: Node, private val parent: Node) :
IAstModification {
override fun perform() {
try {
parent.replaceChildNode(node, replacement)
replacement.linkParents(parent)
} catch (fa: FatalAstException) {
// possibly because of another replacement. Ignore here, we try again later.
}
}
}
class SwapOperands(private val expr: BinaryExpression): IAstModification {
override fun perform() {
require(expr.operator in AssociativeOperators)

View File

@ -3,10 +3,20 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- vm: fix and/or/xor to be bitwise again (need to remove optimization from VariousCleanups too?)
- vm: fix not to be bitwise not instead of boolean not
- 6502: fix not codegen to be bitwise not instead of boolean not (maybe need to change boolean() wrapping / variouscleanups)
all these testable with compiler/test/arithmetic/logical.p8
- 6502: also fix logical and/or/xor routines to just be bitwise routines.
- get rid of logical and/or/xor/not in the codegen (6502+vm)
because bitwise versions + correct use of boolean() operand wrapping are equivalent?
can do this for instance by replacing and/or/xor/not with their bitwise versions &, |, ^, ~
- compiling logical.p8 to virtual with optimization generates a lot larger code as without optimizations.
this is not the case for the 6502 codegen.
- add optimizations: not a or not b -> not(a and b) , not a and not b -> not(a or b)
add unit tests for that.
- bin expr splitter: split logical expressions on ands/ors/xors ?
- add some more optimizations in vmPeepholeOptimizer
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.

View File

@ -124,7 +124,7 @@ All have type b or w.
and reg1, reg2 - reg1 = reg1 bitwise and reg2
or reg1, reg2 - reg1 = reg1 bitwise or reg2
xor reg1, reg2 - reg1 = reg1 bitwise xor reg2
not reg1 - reg1 = boolean not of reg1 (0->1 , ~0 -> 0)
not reg1 - reg1 = bitwise not of reg1 (all bits flipped)
lsrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits + set Carry to shifted bit
asrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits (signed) + set Carry to shifted bit
lsln reg1, reg2 - reg1 = multi-shift reg1 left by reg2 bits + set Carry to shifted bit
@ -138,7 +138,7 @@ roxl reg1 - rotate reg1 left by 1 bits, using
andm reg1 address - memory = memory bitwise and reg1
orm reg1, address - memory = memory bitwise or reg1
xorm reg1, address - memory = memory bitwise xor reg1
notm address - memory = boolean not of that memory (0->1 , ~0 -> 0)
notm address - memory = bitwise not of that memory (all bits flipped)
lsrnm reg1, address - multi-shift memoryright by reg1 bits + set Carry to shifted bit
asrnm reg1, address - multi-shift memory right by reg1 bits (signed) + set Carry to shifted bit
lslnm reg1, address - multi-shift memory left by reg1 bits + set Carry to shifted bit

View File

@ -1132,14 +1132,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun booleanValue(number: UInt): UInt = if(number == 0u) 0u else 1u
private fun booleanValue(number: UByte): UByte = if(number == 0u.toUByte()) 0u else 1u
private fun booleanValue(number: UShort): UShort = if(number == 0u.toUShort()) 0u else 1u
private fun InsAND(i: Instruction) {
val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
val left = booleanValue(leftV)
val right = booleanValue(rightV)
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left and right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left and right).toUShort())
@ -1152,13 +1146,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
val address = i.value!!
when(i.type!!) {
VmDataType.BYTE -> {
val left = booleanValue(memory.getUB(address))
val right = booleanValue(registers.getUB(i.reg1!!))
val left = memory.getUB(address)
val right = registers.getUB(i.reg1!!)
memory.setUB(address, left and right)
}
VmDataType.WORD -> {
val left = booleanValue(memory.getUW(address))
val right = booleanValue(registers.getUW(i.reg1!!))
val left = memory.getUW(address)
val right = registers.getUW(i.reg1!!)
memory.setUW(address, left and right)
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
@ -1167,9 +1161,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
private fun InsOR(i: Instruction) {
val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
val left = booleanValue(leftV)
val right = booleanValue(rightV)
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left or right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left or right).toUShort())
@ -1182,13 +1174,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
val address = i.value!!
when(i.type!!) {
VmDataType.BYTE -> {
val left = booleanValue(memory.getUB(address))
val right = booleanValue(registers.getUB(i.reg1!!))
val left = memory.getUB(address)
val right = registers.getUB(i.reg1!!)
memory.setUB(address, left or right)
}
VmDataType.WORD -> {
val left = booleanValue(memory.getUW(address))
val right = booleanValue(registers.getUW(i.reg1!!))
val left = memory.getUW(address)
val right = registers.getUW(i.reg1!!)
memory.setUW(address, left or right)
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
@ -1197,9 +1189,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
private fun InsXOR(i: Instruction) {
val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
val left = booleanValue(leftV)
val right = booleanValue(rightV)
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left xor right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left xor right).toUShort())
@ -1212,13 +1202,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
val address = i.value!!
when(i.type!!) {
VmDataType.BYTE -> {
val left = booleanValue(memory.getUB(address))
val right = booleanValue(registers.getUB(i.reg1!!))
val left = memory.getUB(address)
val right = registers.getUB(i.reg1!!)
memory.setUB(address, left xor right)
}
VmDataType.WORD -> {
val left = booleanValue(memory.getUW(address))
val right = booleanValue(registers.getUW(i.reg1!!))
val left = memory.getUW(address)
val right = registers.getUW(i.reg1!!)
memory.setUW(address, left xor right)
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
@ -1228,8 +1218,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsNOT(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, if(registers.getUB(i.reg1)==0.toUByte()) 1u else 0u)
VmDataType.WORD -> registers.setUW(i.reg1!!, if(registers.getUW(i.reg1)==0.toUShort()) 1u else 0u)
VmDataType.BYTE -> registers.setUB(i.reg1!!, registers.getUB(i.reg1).inv())
VmDataType.WORD -> registers.setUW(i.reg1!!, registers.getUW(i.reg1).inv())
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
pc++
@ -1238,8 +1228,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsNOTM(i: Instruction) {
val address = i.value!!
when(i.type!!) {
VmDataType.BYTE -> memory.setUB(address, if(memory.getUB(address)==0.toUByte()) 1u else 0u)
VmDataType.WORD -> memory.setUW(address, if(memory.getUW(address)==0.toUShort()) 1u else 0u)
VmDataType.BYTE -> memory.setUB(address, memory.getUB(address).inv())
VmDataType.WORD -> memory.setUW(address, memory.getUW(address).inv())
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
pc++