summaryrefslogtreecommitdiff
path: root/src/Instruction.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/Instruction.scala')
-rw-r--r--src/Instruction.scala306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/Instruction.scala b/src/Instruction.scala
new file mode 100644
index 0000000..7177b89
--- /dev/null
+++ b/src/Instruction.scala
@@ -0,0 +1,306 @@
+package ixee.re.disasm
+
+import ixee.cryptopals.utils.ByteUtils._
+import ixee.cryptopals.utils.FunctionUtils._
+
+import scala.collection.mutable.Map
+
+case class Source(value: Int)
+case class Destination(value: Int)
+
+sealed trait AddressingMode {
+ def modeName: String
+ override def toString: String = modeName
+ def nameifyRegister(reg: Int): String = reg match {
+ case 0 => "pc"
+ case 1 => "sp"
+ case 2 => "sr"
+ case 3 => "cg"
+ case num @ _ => s"r$num"
+ }
+ def stringifyWith(reg: Int): String
+}
+
+sealed trait OneOpAddressing extends AddressingMode
+sealed trait TwoOpAddressing extends AddressingMode
+sealed trait TwoOpSourceAddressing extends TwoOpAddressing
+sealed trait TwoOpDestAddressing extends TwoOpAddressing
+
+object TwoOpSourceAddressingModes {
+ // just alias the apply over to make names look right at call sites.
+ // think hard about refactoring this
+ def apply(register: Int, mode: Int): Option[TwoOpSourceAddressing] = OneOpAddressingModes(register, mode)
+}
+
+object OneOpAddressingModes {
+ case class AddressingMode(as: Int, name: String, stringifier: String => String) extends OneOpAddressing with TwoOpSourceAddressing {
+ def modeName = name
+ def stringifyWith(reg: Int): String = stringifier(nameifyRegister(reg))
+ }
+
+ def apply(register: Int, mode: Int): Option[AddressingMode] = register match {
+ case 0 => PCModes(mode) orElse General(mode)
+ case 2 => SRModes(mode) orElse General(mode)
+ case 3 => CGModes(mode) orElse General(mode)
+ case _ => General(mode)
+ }
+
+ trait Modes {
+ private val modeMap: Map[Int, AddressingMode] = Map()
+ def Mode(as: Int, name: String, stringifier: String => String) = AddressingMode(as, name, stringifier).tap(modeMap += as -> _)
+
+ def apply(x: Int): Option[AddressingMode] = modeMap.get(x)
+ }
+
+ object PCModes extends Modes {
+ Mode(3, "NextWord", reg => s"@$reg+ (next word)")
+ }
+ object SRModes extends Modes {
+ Mode(2, "Const4", reg => "#4")
+ Mode(3, "Const8", reg => "#8")
+ }
+ object CGModes extends Modes {
+ Mode(0, "Const0", reg => "#0")
+ Mode(1, "Const1", reg => "#1")
+ Mode(2, "Const2", reg => "#2")
+ Mode(3, "ConstNeg1", reg => "#-1")
+ }
+ object General extends Modes {
+ Mode(0, "RegisterDirect", ident)
+ Mode(1, "Indexed", reg => s"@PC+($reg)")
+ Mode(2, "RegisterIndirect", reg => s"@$reg")
+ Mode(3, "IndirectAutoInc", reg => s"@$reg+")
+ }
+
+ PCModes
+ SRModes
+ CGModes
+ General
+}
+
+
+object TwoOpDestAddressingModes {
+ case class AddressingMode(as: Int, name: String, stringifier: String => String) extends TwoOpDestAddressing {
+ def modeName = name
+ def stringifyWith(reg: Int): String = stringifier(nameifyRegister(reg))
+ }
+
+ def apply(register: Int, mode: Int): Option[AddressingMode] = register match {
+ case 0 => PCModes(mode) orElse General(mode)
+ case 2 => SRModes(mode) orElse General(mode)
+ case _ => General(mode)
+ }
+
+ trait Modes {
+ private val modeMap: Map[Int, AddressingMode] = Map()
+ def Mode(as: Int, name: String, stringifier: String => String) = AddressingMode(as, name, stringifier).tap(modeMap += as -> _)
+
+ def apply(x: Int): Option[AddressingMode] = modeMap.get(x)
+ }
+
+ object PCModes extends Modes {
+ Mode(1, "Symbolic", reg => s"<??> (symbolic? x(PC)?")
+ }
+ object SRModes extends Modes {
+ Mode(1, "Absolute", reg => s"&@PC+ (value at nextWord)")
+ }
+ object General extends Modes {
+ Mode(0, "RegisterDirect", reg => s"$reg")
+ Mode(1, "Indexed", reg => s"@PC+($reg)")
+ }
+
+ PCModes
+ SRModes
+ General
+}
+
+sealed trait OpCode {
+ def instrName: String
+ override def toString: String = instrName
+}
+
+sealed trait NoOpCode extends OpCode
+sealed trait OneOpCode extends OpCode
+sealed trait TwoOpCode extends OpCode
+
+object Opcodes {
+ def opcodes: Map[Int, OpCode] = noOpCodes ++ oneOpCodes ++ twoOpCodes
+ val noOpCodes: Map[Int, NoOpCode] = Map()
+ val oneOpCodes: Map[Int, OneOpCode] = Map()
+ val twoOpCodes: Map[Int, TwoOpCode] = Map()
+
+ def apply(value: Int) = opcodes.get(value)
+ def noOp(value: Int) = noOpCodes.get(value)
+ def oneOp(value: Int) = oneOpCodes.get(value)
+ def twoOp(value: Int) = twoOpCodes.get(value)
+
+ object NoOpCodes {
+ val Prefix = 0x01
+ case class Code(value: Byte, name: String) extends NoOpCode { def instrName = name }
+ object Code {
+ def apply(value: Int, name: String) = new Code(value.toByte, name).tap(code => noOpCodes += ((((Prefix << 3) + value) << 10) -> code))
+ }
+
+ Code(0x00, "JNE")
+ Code(0x01, "JEQ")
+ Code(0x02, "JNC")
+ Code(0x03, "JC ")
+ Code(0x04, "JN ")
+ Code(0x05, "JGE")
+ Code(0x06, "JL ")
+ Code(0x07, "JMP")
+ }
+
+ object OneOpCodes {
+ val Prefix = 0x04
+ case class Code(value: Byte, name: String) extends OneOpCode { def instrName = name }
+ object Code {
+ def apply(value: Int, name: String) = new Code(value.toByte, name).tap(code => oneOpCodes += ((((Prefix << 3) + value) << 7) -> code))
+ }
+
+ Code(0x00, "RRC")
+ Code(0x01, "SWPB")
+ Code(0x02, "RRA")
+ Code(0x03, "SXT")
+ Code(0x04, "PUSH")
+ Code(0x05, "CALL")
+ Code(0x06, "RETI")
+// Code(0x07, "NotImplemented")
+ }
+
+ object TwoOpCodes {
+ val Prefix = 0x1
+ case class Code(value: Byte, name: String) extends TwoOpCode { def instrName = name }
+ object Code {
+ def apply(value: Int, name: String) = new Code(value.toByte, name).tap(code => twoOpCodes += ((value @<< 12) -> code))
+ }
+
+ Code(0x04, "MOV")
+ Code(0x05, "ADD")
+ Code(0x06, "ADDC")
+ Code(0x07, "SUBC")
+ Code(0x08, "SUB")
+ Code(0x09, "CMP")
+ Code(0x0a, "DADD")
+ Code(0x0b, "BIT")
+ Code(0x0c, "BIC")
+ Code(0x0d, "BIS")
+ Code(0x0e, "XOR")
+ Code(0x0f, "AND")
+ }
+
+ // forces the various opcode bodies to be evaluated. kinda weird, but it works so welp.
+ NoOpCodes
+ OneOpCodes
+ TwoOpCodes
+}
+
+sealed trait Instruction
+
+case class NoOpInst(code: NoOpCode, offset: Int) extends Instruction {
+ override def toString: String = {
+ s"$code -> $offset"
+ }
+}
+case class OneOpInst(code: OneOpCode, byte: Boolean, dest: Int, addressMode: OneOpAddressing) extends Instruction {
+ override def toString: String = {
+ val byteStr = if (byte) ".B" else " "
+ s"$code$byteStr ${addressMode.stringifyWith(dest)}"
+ }
+}
+case class TwoOpInst(code: TwoOpCode, byte: Boolean, src: Int, srcMode: TwoOpSourceAddressing, dest: Int, dstMode: TwoOpDestAddressing) extends Instruction {
+ override def toString: String = {
+ val byteStr = if (byte) ".B" else " "
+ s"$code$byteStr ${srcMode.stringifyWith(src)}, ${dstMode.stringifyWith(dest)}"
+ }
+}
+
+object NoOp {
+ def info(bytes: (Byte, Byte)): (Int, Int) = {
+ val op = (bytes._1 & 0xfc) >> 2
+ val offset = ((bytes._1 & 0x03) << 8) | bytes._2
+ (op, offset)
+ }
+
+ def apply(bytes: (Byte, Byte)): Option[NoOpInst] = {
+ val (opVal, offset) = info(bytes)
+ for {
+ opcode <- Opcodes.noOp(opVal << 10)
+ } yield NoOpInst(opcode, offset)
+ }
+}
+
+object OneOp {
+ def info(bytes: (Byte, Byte)): (Int, Int, Int, Int) = {
+ val dest = (bytes._2 & 0x0f)
+ val destMode = ((bytes._2 & 0x30) >> 4)
+ val byteBit = ((bytes._2 & 0x40) >> 6)
+ val opcode = ((bytes._1 & 0xff) << 1) | ((bytes._2 & 0x80) >> 7)
+ (opcode, byteBit, dest, destMode)
+ }
+
+ def apply(bytes: (Byte, Byte)): Option[OneOpInst] = {
+ val (opVal, byteInstr, dest, rawDestMode) = info(bytes)
+ for {
+ opcode <- Opcodes.oneOp(opVal << 7)
+ isByteOp = if (byteInstr == 1) true else false
+ destMode <- OneOpAddressingModes(dest, rawDestMode)
+ } yield OneOpInst(opcode, isByteOp, dest, destMode)
+ }
+}
+
+object TwoOp {
+ def info(bytes: (Byte, Byte)): (Int, Int, Int, Int, Int, Int) = {
+ val dest = (bytes._2 & 0x0f)
+ val sourceMode = (bytes._2 & 0x30) >> 4
+ val byteBit = (bytes._2 & 0x40) >> 6
+ val destMode = (bytes._2 & 0x80) >> 7
+ val source = (bytes._1 & 0x0f)
+ val opcode = (bytes._1 & 0xf0) >> 4
+ (opcode, byteBit, source, destMode, dest, sourceMode)
+ }
+
+ def apply(bytes: (Byte, Byte)): Option[TwoOpInst] = {
+ val (opVal, byteInstr, source, rawDestMode, dest, rawSourceMode) = info(bytes)
+
+ for {
+ opcode <- Opcodes.twoOp(opVal << 12)
+ isByteOp = if (byteInstr == 1) true else false
+ destMode <- TwoOpDestAddressingModes(dest, rawDestMode)
+ sourceMode <- TwoOpSourceAddressingModes(source, rawSourceMode)
+ } yield TwoOpInst(opcode, isByteOp, source, sourceMode, dest, destMode)
+ }
+}
+
+object Instruction {
+ def apply(instruction: (Byte, Byte)): Option[Instruction] = NoOp(instruction) orElse OneOp(instruction) orElse TwoOp(instruction)
+ def littleEndian(instruction: String): Option[Instruction] = {
+ val instr = Integer.parseInt(instruction, 16)
+ val hi = (instr & 0xff00) >> 8
+ val lo = instr & 0x00ff
+ apply((hi.toByte, lo.toByte))
+ }
+ def apply(instruction: String): Option[Instruction] = {
+ val instr = Integer.parseInt(instruction, 16)
+ val hi = (instr & 0xff00) >> 8
+ val lo = instr & 0x00ff
+ apply((lo.toByte, hi.toByte))
+ }
+
+ def noOp(instruction: (Byte, Byte)): Option[NoOpInst] = NoOp.apply(instruction)
+
+ def oneOp(instruction: (Byte, Byte)): Option[OneOpInst] = OneOp.apply(instruction)
+
+ def twoOp(instruction: (Byte, Byte)): Option[TwoOpInst] = TwoOp.apply(instruction)
+
+ def isNoOp(instruction: (Byte ,Byte)) = noOp(instruction).isDefined
+
+ def isOneOp(instruction: (Byte, Byte)) = oneOp(instruction).isDefined
+
+ def isTwoOp(instruction: (Byte, Byte)) = twoOp(instruction).isDefined
+}
+
+sealed trait ByteOp {
+ override def toString = s"(Single Byte: ${super.toString})"
+}
+