Skip to content

Commit

Permalink
Add MIPS II instruction set
Browse files Browse the repository at this point in the history
  • Loading branch information
kotcrab committed May 31, 2018
1 parent b7f93c6 commit 1226158
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#### Version: 1.2
- Added MIPS II instruction set (including FPU instructions)

#### Version: 1.1
- Added FPU instruction set
- Updated instruction set to closer match reference document
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ kmips
-----

kmips is a MIPS assembler that is invoked directly from Kotlin code. It implements
MIPS I instruction set, including FPU (coprocessor 1) instructions. The main purpose of kmips
MIPS II instruction set, including FPU (coprocessor 1) instructions. The main purpose of kmips
is to provide simple way of writing code patches for compiled executables. It was successfully used
in few fan translations and game modding projects.


kmips is available from Maven Central repository
```groovy
compile "com.kotcrab.kmips:kmips:1.1"
compile "com.kotcrab.kmips:kmips:1.2"
```

##### Example code
Expand Down
93 changes: 84 additions & 9 deletions src/main/kotlin/kmips/Assembler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Assembler(val startPc: Int, val endianness: Endianness) {
private set
private val instructions = mutableListOf<Instruction>()

// MIPS I
// MIPS II

fun lb(rt: Reg, offset: Int, base: Reg) = emit(IInstruction(0b100_000, base, rt, offset))
fun lbu(rt: Reg, offset: Int, base: Reg) = emit(IInstruction(0b100_100, base, rt, offset))
Expand All @@ -44,6 +44,9 @@ class Assembler(val startPc: Int, val endianness: Endianness) {
fun swl(rt: Reg, offset: Int, base: Reg) = emit(IInstruction(0b101_010, base, rt, offset))
fun swr(rt: Reg, offset: Int, base: Reg) = emit(IInstruction(0b101_110, base, rt, offset))

fun ll(rt: Reg, offset: Int, base: Reg) = emit(IInstruction(0b110_000, base, rt, offset))
fun sc(rt: Reg, offset: Int, base: Reg) = emit(IInstruction(0b111_000, base, rt, offset))

fun addi(rt: Reg, rs: Reg, imm: Int) = emit(IInstruction(0b001_000, rs, rt, imm))
fun addiu(rt: Reg, rs: Reg, imm: Int) = emit(IInstruction(0b001_001, rs, rt, imm))
fun slti(rt: Reg, rs: Reg, imm: Int) = emit(IInstruction(0b001_010, rs, rt, imm))
Expand Down Expand Up @@ -91,18 +94,42 @@ class Assembler(val startPc: Int, val endianness: Endianness) {
fun bne(rs: Reg, rt: Reg, label: Label) = emitBranchInstruction(0b000_101, rs, rt, label)
fun blez(rs: Reg, label: Label) = emitBranchInstruction(0b000_110, rs, Reg.zero, label)
fun bgtz(rs: Reg, label: Label) = emitBranchInstruction(0b000_111, rs, Reg.zero, label)

fun bltz(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs, Reg.zero, label)
fun bgez(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs, Reg.at, label)
fun bltzal(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs, Reg.s0, label)
fun bgezal(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs, Reg.s1, label)
fun beql(rs: Reg, rt: Reg, label: Label) = emitBranchInstruction(0b010_100, rs, rt, label)
fun bnel(rs: Reg, rt: Reg, label: Label) = emitBranchInstruction(0b010_101, rs, rt, label)
fun blezl(rs: Reg, label: Label) = emitBranchInstruction(0b010_110, rs, Reg.zero, label)
fun bgtzl(rs: Reg, label: Label) = emitBranchInstruction(0b010_111, rs, Reg.zero, label)

fun bltz(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs.id, 0b00000, label)
fun bgez(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs.id, 0b00001, label)
fun bltzal(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs.id, 0b10000, label)
fun bgezal(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs.id, 0b10001, label)
fun bltzl(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs.id, 0b00010, label)
fun bgezl(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs.id, 0b00011, label)
fun bltzall(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs.id, 0b10010, label)
fun bgezall(rs: Reg, label: Label) = emitBranchInstruction(0b000_001, rs.id, 0b10011, label)

fun syscall(code: Int) = emit(CodeInstruction(0, code, 0b001_100))
fun `break`(code: Int) = emit(CodeInstruction(0, code, 0b001_101))

fun tge(rs: Reg, rt: Reg, code: Int = 0x200) = emit(RInstruction(0, rs.id, rt.id, code ushr 5, code and 0x1F, 0b110_000))
fun tgeu(rs: Reg, rt: Reg, code: Int = 0x200) = emit(RInstruction(0, rs.id, rt.id, code ushr 5, code and 0x1F, 0b110_001))
fun tlt(rs: Reg, rt: Reg, code: Int = 0x200) = emit(RInstruction(0, rs.id, rt.id, code ushr 5, code and 0x1F, 0b110_010))
fun tltu(rs: Reg, rt: Reg, code: Int = 0x200) = emit(RInstruction(0, rs.id, rt.id, code ushr 5, code and 0x1F, 0b110_011))
fun teq(rs: Reg, rt: Reg, code: Int = 0x200) = emit(RInstruction(0, rs.id, rt.id, code ushr 5, code and 0x1F, 0b110_100))
fun tne(rs: Reg, rt: Reg, code: Int = 0x200) = emit(RInstruction(0, rs.id, rt.id, code ushr 5, code and 0x1F, 0b110_110))

fun tgei(rs: Reg, imm: Int) = emit(IInstruction(0b000_001, rs.id, 0b01000, imm))
fun tgeiu(rs: Reg, imm: Int) = emit(IInstruction(0b000_001, rs.id, 0b01001, imm))
fun tlti(rs: Reg, imm: Int) = emit(IInstruction(0b000_001, rs.id, 0b01010, imm))
fun tltiu(rs: Reg, imm: Int) = emit(IInstruction(0b000_001, rs.id, 0b01011, imm))
fun teqi(rs: Reg, imm: Int) = emit(IInstruction(0b000_001, rs.id, 0b01100, imm))
fun tnei(rs: Reg, imm: Int) = emit(IInstruction(0b000_001, rs.id, 0b01110, imm))

fun sync(stype: Int = 0) = emit(RInstruction(0, 0, 0, 0, stype, 0b001_111))

fun nop() = emit(NopInstruction())

// FPU
// FPU (MIPS II)

/** FPU (coprocessor 1) */
private val COP1 = 0b010_001
Expand All @@ -129,6 +156,11 @@ class Assembler(val startPc: Int, val endianness: Endianness) {
val div = DivFpu()
val abs = AbsFpu()
val neg = NegFpu()
val sqrt = SqrtFpu()
val round = RoundFpu()
val trunc = TruncFpu()
val ceil = CeilFpu()
val floor = FloorFpu()

inner class AddFpu {
fun s(fd: FpuReg, fs: FpuReg, ft: FpuReg) = emit(FpuInstruction(COP1, FMT_S, ft, fs, fd, 0b000_000))
Expand Down Expand Up @@ -160,6 +192,47 @@ class Assembler(val startPc: Int, val endianness: Endianness) {
fun d(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_D, FpuReg.f0, fs, fd, 0b000_111))
}

inner class SqrtFpu {
fun s(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_S, FpuReg.f0, fs, fd, 0b000_100))
fun d(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_D, FpuReg.f0, fs, fd, 0b000_100))
}

inner class RoundFpu {
val w = RoundWFpu()

inner class RoundWFpu {
fun s(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_S, FpuReg.f0, fs, fd, 0b001_100))
fun d(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_D, FpuReg.f0, fs, fd, 0b001_100))
}
}

inner class TruncFpu {
val w = TruncWFpu()

inner class TruncWFpu {
fun s(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_S, FpuReg.f0, fs, fd, 0b001_101))
fun d(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_D, FpuReg.f0, fs, fd, 0b001_101))
}
}

inner class CeilFpu {
val w = CeilWFpu()

inner class CeilWFpu {
fun s(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_S, FpuReg.f0, fs, fd, 0b001_110))
fun d(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_D, FpuReg.f0, fs, fd, 0b001_110))
}
}

inner class FloorFpu {
val w = FloorWFpu()

inner class FloorWFpu {
fun s(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_S, FpuReg.f0, fs, fd, 0b001_111))
fun d(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_D, FpuReg.f0, fs, fd, 0b001_111))
}
}

val c = CompareFpu()

inner class CompareFpu {
Expand Down Expand Up @@ -213,8 +286,10 @@ class Assembler(val startPc: Int, val endianness: Endianness) {
fun d(fd: FpuReg, fs: FpuReg) = emit(FpuInstruction(COP1, FMT_D, FpuReg.f0, fs, fd, 0b000_110))
}

fun bc1f(label: Label) = emitBranchInstruction(COP1, 0b01000, 0, label)
fun bc1t(label: Label) = emitBranchInstruction(COP1, 0b01000, 1, label)
fun bc1f(label: Label) = emitBranchInstruction(COP1, 0b01000, 0b00, label)
fun bc1t(label: Label) = emitBranchInstruction(COP1, 0b01000, 0b01, label)
fun bc1tl(label: Label) = emitBranchInstruction(COP1, 0b01000, 0b11, label)
fun bc1fl(label: Label) = emitBranchInstruction(COP1, 0b01000, 0b10, label)

// Pseudo instructions / aliases

Expand Down
39 changes: 39 additions & 0 deletions src/test/kotlin/kmips/AssemblerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class AssemblerTest {
@Test fun testSwl() = testInstruction("A89000CD", { swl(s0, 0xCD, a0) })
@Test fun testSwr() = testInstruction("B89000CD", { swr(s0, 0xCD, a0) })

@Test fun testLl() = testInstruction("C09000CD", { ll(s0, 0xCD, a0) })
@Test fun testSc() = testInstruction("E09000CD", { sc(s0, 0xCD, a0) })

@Test fun testAddi() = testInstruction("209000CD", { addi(s0, a0, 0xCD) })
@Test fun testAddiu() = testInstruction("249000CD", { addiu(s0, a0, 0xCD) })
@Test fun testSlti() = testInstruction("289000CD", { slti(s0, a0, 0xCD) })
Expand Down Expand Up @@ -72,15 +75,39 @@ class AssemblerTest {
@Test fun testBne() = testBranchInstruction("16040001", { bne(s0, a0, it) })
@Test fun testBlez() = testBranchInstruction("1A000001", { blez(s0, it) })
@Test fun testBgtz() = testBranchInstruction("1E000001", { bgtz(s0, it) })
@Test fun testBeql() = testBranchInstruction("52040001", { beql(s0, a0, it) })
@Test fun testBnel() = testBranchInstruction("56040001", { bnel(s0, a0, it) })
@Test fun testBlezl() = testBranchInstruction("5A000001", { blezl(s0, it) })
@Test fun testBgtzl() = testBranchInstruction("5E000001", { bgtzl(s0, it) })

@Test fun testBltz() = testBranchInstruction("06000001", { bltz(s0, it) })
@Test fun testBgez() = testBranchInstruction("06010001", { bgez(s0, it) })
@Test fun testBltzal() = testBranchInstruction("06100001", { bltzal(s0, it) })
@Test fun testBgezal() = testBranchInstruction("06110001", { bgezal(s0, it) })
@Test fun testBltzl() = testBranchInstruction("06020001", { bltzl(s0, it) })
@Test fun testBgezl() = testBranchInstruction("06030001", { bgezl(s0, it) })
@Test fun testBltzall() = testBranchInstruction("06120001", { bltzall(s0, it) })
@Test fun testBgezall() = testBranchInstruction("06130001", { bgezall(s0, it) })

@Test fun testSyscall() = testInstruction("0033734C", { syscall(0xCDCD) })
@Test fun testBreak() = testInstruction("0033734D", { `break`(0xCDCD) })

@Test fun testTge() = testInstruction("02048030", { tge(s0, a0) })
@Test fun testTgeu() = testInstruction("02048031", { tgeu(s0, a0) })
@Test fun testTlt() = testInstruction("02048032", { tlt(s0, a0) })
@Test fun testTltu() = testInstruction("02048033", { tltu(s0, a0) })
@Test fun testTeq() = testInstruction("02048034", { teq(s0, a0) })
@Test fun testTne() = testInstruction("02048036", { tne(s0, a0) })

@Test fun testTgei() = testInstruction("060800CD", { tgei(s0, 0xCD) })
@Test fun testTgeiu() = testInstruction("060900CD", { tgeiu(s0, 0xCD) })
@Test fun testTlti() = testInstruction("060A00CD", { tlti(s0, 0xCD) })
@Test fun testTltiu() = testInstruction("060B00CD", { tltiu(s0, 0xCD) })
@Test fun testTeqi() = testInstruction("060C00CD", { teqi(s0, 0xCD) })
@Test fun testTnei() = testInstruction("060E00CD", { tnei(s0, 0xCD) })

@Test fun testSync() = testInstruction("0000000F", { sync(0) })

@Test fun testNop() = testInstruction("00000000", { nop() })

@Test fun testLwc1() = testInstruction("C48C00CD", { lwc1(f12, 0xCD, a0) })
Expand All @@ -103,6 +130,16 @@ class AssemblerTest {
@Test fun testFpuAbsD() = testInstruction("46206105", { abs.d(f4, f12) })
@Test fun testFpuNegS() = testInstruction("46006107", { neg.s(f4, f12) })
@Test fun testFpuNegD() = testInstruction("46206107", { neg.d(f4, f12) })
@Test fun testFpuSqrtS() = testInstruction("46006104", { sqrt.s(f4, f12) })
@Test fun testFpuSqrtD() = testInstruction("46206104", { sqrt.d(f4, f12) })
@Test fun testFpuRoundWS() = testInstruction("4600610C", { round.w.s(f4, f12) })
@Test fun testFpuRoundWD() = testInstruction("4620610C", { round.w.d(f4, f12) })
@Test fun testFpuTruncWS() = testInstruction("4600610D", { trunc.w.s(f4, f12) })
@Test fun testFpuTruncWD() = testInstruction("4620610D", { trunc.w.d(f4, f12) })
@Test fun testFpuCeilWS() = testInstruction("4600610E", { ceil.w.s(f4, f12) })
@Test fun testFpuCeilWD() = testInstruction("4620610E", { ceil.w.d(f4, f12) })
@Test fun testFpuFloorWS() = testInstruction("4600610F", { floor.w.s(f4, f12) })
@Test fun testFpuFloorWD() = testInstruction("4620610F", { floor.w.d(f4, f12) })

@Test fun testFpuCondEqS() = testInstruction("460C2032", { c.eq.s(f4, f12) })
@Test fun testFpuCondEqD() = testInstruction("462C2032", { c.eq.d(f4, f12) })
Expand All @@ -123,6 +160,8 @@ class AssemblerTest {

@Test fun testBc1f() = testBranchInstruction("45000001", { bc1f(it) })
@Test fun testBc1t() = testBranchInstruction("45010001", { bc1t(it) })
@Test fun testBc1tl() = testBranchInstruction("45030001", { bc1tl(it) })
@Test fun testBc1fl() = testBranchInstruction("45020001", { bc1fl(it) })

@Test fun testB() = testBranchInstruction("10000001", { b(it) })
@Test fun testBlt() = testBranchInstruction("0204082A14200001", { blt(a0, s0, it) })
Expand Down

0 comments on commit 1226158

Please sign in to comment.