prog8/intermediate/test/TestInstructions.kt

150 lines
5.7 KiB
Kotlin

import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.throwables.shouldThrowWithMessage
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import prog8.intermediate.*
class TestInstructions: FunSpec({
test("simple") {
val ins = IRInstruction(Opcode.NOP)
ins.opcode shouldBe Opcode.NOP
ins.type shouldBe null
ins.reg1direction shouldBe OperandDirection.UNUSED
ins.fpReg1direction shouldBe OperandDirection.UNUSED
ins.reg1 shouldBe null
ins.reg2 shouldBe null
ins.address shouldBe null
ins.immediate shouldBe null
ins.immediateFp shouldBe null
ins.labelSymbol shouldBe null
ins.toString() shouldBe "nop"
}
test("with value") {
val ins = IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 0, address = 99)
ins.opcode shouldBe Opcode.ADD
ins.type shouldBe IRDataType.BYTE
ins.reg1direction shouldBe OperandDirection.READWRITE
ins.fpReg1direction shouldBe OperandDirection.UNUSED
ins.reg1 shouldBe 42
ins.reg2 shouldBe null
ins.address shouldBe 99
ins.immediate shouldBe 0
ins.immediateFp shouldBe null
ins.labelSymbol shouldBe null
ins.toString() shouldBe "add.b r42,0,$63"
}
test("with label") {
val ins = IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=11, immediate = 0, labelSymbol = "a.b.c")
ins.opcode shouldBe Opcode.ADD
ins.type shouldBe IRDataType.WORD
ins.reg1direction shouldBe OperandDirection.READWRITE
ins.fpReg1direction shouldBe OperandDirection.UNUSED
ins.reg1 shouldBe 11
ins.reg2 shouldBe null
ins.address shouldBe null
ins.immediate shouldBe 0
ins.immediateFp shouldBe null
ins.labelSymbol shouldBe "a.b.c"
ins.toString() shouldBe "add.w r11,0,a.b.c"
}
test("with output registers") {
val ins = IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=11, reg2=22)
ins.opcode shouldBe Opcode.ADDR
ins.type shouldBe IRDataType.WORD
ins.reg1direction shouldBe OperandDirection.READWRITE
ins.reg2direction shouldBe OperandDirection.READ
ins.fpReg1direction shouldBe OperandDirection.UNUSED
ins.fpReg2direction shouldBe OperandDirection.UNUSED
ins.reg1 shouldBe 11
ins.reg2 shouldBe 22
ins.address shouldBe null
ins.immediate shouldBe null
ins.immediateFp shouldBe null
ins.labelSymbol shouldBe null
ins.toString() shouldBe "addr.w r11,r22"
val ins2 = IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=11, reg2=22)
ins2.opcode shouldBe Opcode.SQRT
ins2.type shouldBe IRDataType.BYTE
ins2.reg1direction shouldBe OperandDirection.WRITE
ins2.reg2direction shouldBe OperandDirection.READ
ins2.fpReg1direction shouldBe OperandDirection.UNUSED
ins2.fpReg2direction shouldBe OperandDirection.UNUSED
ins2.reg1 shouldBe 11
ins2.reg2 shouldBe 22
ins.address shouldBe null
ins.immediate shouldBe null
ins.immediateFp shouldBe null
ins2.labelSymbol shouldBe null
ins2.toString() shouldBe "sqrt.b r11,r22"
}
test("with float regs") {
val ins = IRInstruction(Opcode.FSIN, IRDataType.FLOAT, fpReg1 = 1, fpReg2 = 2)
ins.opcode shouldBe Opcode.FSIN
ins.type shouldBe IRDataType.FLOAT
ins.reg1direction shouldBe OperandDirection.UNUSED
ins.reg2direction shouldBe OperandDirection.UNUSED
ins.fpReg1direction shouldBe OperandDirection.WRITE
ins.fpReg2direction shouldBe OperandDirection.READ
ins.fpReg1 shouldBe 1
ins.fpReg2 shouldBe 2
ins.reg1 shouldBe null
ins.reg2 shouldBe null
ins.address shouldBe null
ins.immediate shouldBe null
ins.immediateFp shouldBe null
ins.labelSymbol shouldBe null
ins.toString() shouldBe "fsin.f fr1,fr2"
}
test("missing type should fail") {
shouldThrow<IllegalArgumentException> {
IRInstruction(Opcode.ADD, reg1=42, address=99)
}
}
test("missing registers should fail") {
shouldThrowWithMessage<IllegalArgumentException>("missing reg1") {
IRInstruction(Opcode.ADD, IRDataType.BYTE, immediate = 0, address=99)
}
}
test("missing address should fail") {
shouldThrowWithMessage<IllegalArgumentException>("missing an address or labelsymbol") {
IRInstruction(Opcode.INCM, IRDataType.BYTE)
}
}
test("all instructionformats") {
instructionFormats.size shouldBe Opcode.entries.size
Opcode.entries.forEach {
val fmt = instructionFormats.getValue(it)
fmt.values.forEach { format ->
require(format.reg2==OperandDirection.UNUSED || format.reg2==OperandDirection.READ) {"reg2 can only be used as input"}
require(format.fpReg2==OperandDirection.UNUSED || format.fpReg2==OperandDirection.READ) {"fpReg2 can only be used as input"}
}
}
}
test("with symbol offset") {
val i1 = IRInstruction(Opcode.ADDM, IRDataType.BYTE, reg1 = 1, labelSymbol = "symbol", symbolOffset = 99)
i1.labelSymbol shouldBe "symbol"
i1.labelSymbolOffset shouldBe 99
val i2 = IRInstruction(Opcode.ADDM, IRDataType.BYTE, reg1 = 1, labelSymbol = "symbol", symbolOffset = 0)
i2.labelSymbol shouldBe "symbol"
i2.labelSymbolOffset shouldBe null
shouldThrowWithMessage<IllegalArgumentException>("labelsymbol offset inconsistency") {
IRInstruction(Opcode.ADDR, IRDataType.BYTE, reg1 = 1, reg2 = 2, symbolOffset = 99)
}
}
})