diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/AddressingModes.scala | 52 | ||||
| -rw-r--r-- | src/Instruction.scala | 77 | ||||
| -rw-r--r-- | src/main.scala | 6 | 
3 files changed, 109 insertions, 26 deletions
diff --git a/src/AddressingModes.scala b/src/AddressingModes.scala index f511768..30ad696 100644 --- a/src/AddressingModes.scala +++ b/src/AddressingModes.scala @@ -6,8 +6,9 @@ import ixee.cryptopals.utils.FunctionUtils._  import scala.collection.mutable.Map  sealed trait AddressingMode { -  def modeName: String -  override def toString: String = modeName +  def name: String +  def wide: Boolean +  override def toString: String = name    def nameifyRegister(reg: Int): String = reg match {      case 0 => "pc"      case 1 => "sp" @@ -15,9 +16,18 @@ sealed trait AddressingMode {      case 3 => "cg"      case num @ _ => s"r$num"    } +} + +sealed trait NormMode extends AddressingMode { +  def wide = false    def stringifyWith(reg: Int): String  } +sealed trait WideMode extends AddressingMode { +  def wide = true +  def stringifyWith(reg: Int, word: Int): String +} +  sealed trait OneOpAddressing extends AddressingMode  sealed trait TwoOpAddressing extends AddressingMode  sealed trait TwoOpSourceAddressing extends TwoOpAddressing @@ -30,10 +40,7 @@ object TwoOpSourceAddressingModes {  }  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)) -  } +  trait AddressingMode extends OneOpAddressing with TwoOpSourceAddressing    def apply(register: Int, mode: Int): Option[AddressingMode] = register match {      case 0 => PCModes(mode) orElse General(mode) @@ -44,15 +51,21 @@ object OneOpAddressingModes {    trait Modes {      private val modeMap: Map[Int, AddressingMode] = Map() -    case class Mode(as: Int, name: String, stringifier: String => String) extends AddressingMode(as, name, stringifier) { -      def Mode(as: Int, name: String, stringifier: String => String) = this(as, name, stringifier).tap(modeMap += as -> _) + +    case class Mode(as: Int, name: String, stringifier: String => String) extends AddressingMode with NormMode { +      modeMap += as -> this +      def stringifyWith(reg: Int): String = stringifier(nameifyRegister(reg)) +    } +    case class ExtraByteMode(as: Int, name: String, stringifier: (String, Int) => String) extends AddressingMode with WideMode { +      modeMap += as -> this +      def stringifyWith(reg: Int, const: Int): String = stringifier(nameifyRegister(reg), const)      }      def apply(x: Int): Option[AddressingMode] = modeMap.get(x)    }    object PCModes extends Modes { -    Mode(3, "NextWord", reg => s"@PC+ (next word)") +    ExtraByteMode(3, "NextWord", (reg, byte) => s"${if (byte < 0) "-" else ""}0x${"%x".format(Math.abs(byte))}")    }    object SRModes extends Modes {      Mode(2, "Const4", reg => "#4") @@ -66,7 +79,7 @@ object OneOpAddressingModes {    }    object General extends Modes {      Mode(0, "RegisterDirect", ident) -    Mode(1, "Indexed", reg => s"@PC+($reg)") +    ExtraByteMode(1, "Indexed", (reg, byte) => s"${if (byte < 0) "-" else ""}0x${"%x".format(Math.abs(byte))}($reg)")      Mode(2, "RegisterIndirect", reg => s"@$reg")      Mode(3, "IndirectAutoInc", reg => s"@$reg+")    } @@ -79,10 +92,7 @@ object OneOpAddressingModes {  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)) -  } +  trait AddressingMode extends TwoOpDestAddressing    def apply(register: Int, mode: Int): Option[AddressingMode] = register match {      case 0 => PCModes(mode) orElse General(mode) @@ -92,7 +102,15 @@ object TwoOpDestAddressingModes {    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 -> _) + +    case class Mode(as: Int, name: String, stringifier: String => String) extends AddressingMode with NormMode { +      modeMap += as -> this +      def stringifyWith(reg: Int): String = stringifier(nameifyRegister(reg)) +    } +    case class ExtraByteMode(as: Int, name: String, stringifier: (String, Int) => String) extends AddressingMode with WideMode { +      modeMap += as -> this +      def stringifyWith(reg: Int, const: Int): String = stringifier(nameifyRegister(reg), const) +    }      def apply(x: Int): Option[AddressingMode] = modeMap.get(x)    } @@ -101,11 +119,11 @@ object TwoOpDestAddressingModes {      Mode(1, "Symbolic", reg => s"<??> (symbolic? x(PC)?")    }    object SRModes extends Modes { -    Mode(1, "Absolute", reg => s"&@PC+ (value at nextWord)") +    ExtraByteMode(1, "Absolute", (reg, byte) => s"&${"%4x".format(byte)}")    }    object General extends Modes {      Mode(0, "RegisterDirect", reg => s"$reg") -    Mode(1, "Indexed", reg => s"@PC+($reg)") +    ExtraByteMode(1, "Indexed", (reg, byte) => s"${if (byte < 0) "-" else ""}0x${"%x".format(Math.abs(byte))}($reg)")    }    PCModes diff --git a/src/Instruction.scala b/src/Instruction.scala index 8a4e018..7723ef7 100644 --- a/src/Instruction.scala +++ b/src/Instruction.scala @@ -3,25 +3,54 @@ package ixee.re.disasm  import ixee.cryptopals.utils.ByteUtils._  import ixee.cryptopals.utils.FunctionUtils._ -import scala.collection.mutable.Map +import scala.collection.mutable.{ArrayBuffer, Map} -sealed trait Instruction +sealed trait Instruction { +  def isDone: Boolean +  def giveWord(word: Int): Unit +}  case class NoOpInst(code: NoOpCode, offset: Int) extends Instruction {    override def toString: String = {      s"$code -> $offset"    } +  def giveWord(word: Int) = throw new RuntimeException("Too many bytes!") +  def isDone = true  }  case class OneOpInst(code: OneOpCode, byte: Boolean, dest: Int, addressMode: OneOpAddressing) extends Instruction { +  private var extraWord: Option[Int] = None +  def neededWords = Seq(addressMode).count(_.wide) +  def giveWord(word: Int) = +    extraWord.fold(extraWord = Some(word))(_ => throw new RuntimeException("Too many bytes!")) +  def isDone = neededWords == Seq(extraWord).flatten.length +    override def toString: String = {      val byteStr = if (byte) ".B" else "  " -    s"$code$byteStr ${addressMode.stringifyWith(dest)}" +    s"$code$byteStr ${addressMode match { +      case mode: NormMode => mode.stringifyWith(dest) +      case mode: WideMode => mode.stringifyWith(dest, extraWord.get) +    }}"    }  }  case class TwoOpInst(code: TwoOpCode, byte: Boolean, src: Int, srcMode: TwoOpSourceAddressing, dest: Int, dstMode: TwoOpDestAddressing) extends Instruction { +  private var extraWord1: Option[Int] = None +  private var extraWord2: Option[Int] = None +  def neededWords = Seq(srcMode, dstMode).count(_.wide) +  def giveWord(word: Int) = +    extraWord1.fold(extraWord1 = Some(word))(_ => +      extraWord2.fold(extraWord2 = Some(word))(_ => throw new RuntimeException("Too many bytes!")) +    ) +  def isDone = neededWords == Seq(extraWord1, extraWord2).flatten.length +    override def toString: String = {      val byteStr = if (byte) ".B" else "  " -    s"$code$byteStr ${srcMode.stringifyWith(src)}, ${dstMode.stringifyWith(dest)}" +    s"$code$byteStr ${srcMode match { +      case mode: NormMode => mode.stringifyWith(src) +      case mode: WideMode => mode.stringifyWith(src, extraWord1.get) +    }}, ${dstMode match { +      case mode: NormMode => mode.stringifyWith(dest) +      case mode: WideMode => mode.stringifyWith(dest, extraWord2.orElse(if (srcMode.wide) None else extraWord1).get) +    }}"    }  } @@ -103,10 +132,48 @@ object Instruction {    def twoOp(instruction: (Byte, Byte)): Option[TwoOpInst] = TwoOp.apply(instruction) -  def isNoOp(instruction: (Byte ,Byte)) = noOp(instruction).isDefined +  def isNoOp(instruction: (Byte, Byte)) = noOp(instruction).isDefined    def isOneOp(instruction: (Byte, Byte)) = oneOp(instruction).isDefined    def isTwoOp(instruction: (Byte, Byte)) = twoOp(instruction).isDefined  } +class InstructionParser(bytes: Seq[Byte]) { +  val instructions = scala.collection.mutable.ArrayBuffer[Instruction]() +  val bytePair = scala.collection.mutable.ArrayBuffer[Byte]() + +  for (byte <- bytes) { +    bytePair match { +      case ArrayBuffer() => bytePair += byte +      case ArrayBuffer(first) => { +        val second = byte +        def parseInstr = { +          instructions += Instruction((second, first)).getOrElse { throw new RuntimeException(s"Parse failure! Bytes = $first, $second") } +          bytePair.clear +        } + +        val lastIsDone = instructions.lastOption.map(_.isDone).getOrElse(true) + +        instructions.lastOption.map { instr => +          if (instr.isDone) { +            parseInstr +          } else { +            instr.giveWord(second * 256 + first) +            bytePair.clear +          } +        } getOrElse { parseInstr } +      } +      case buffer @ _ => throw new RuntimeException("Byte array overflow: " + buffer) +    } +  } + +  instructions.lastOption match { +    case Some(instr) if !instr.isDone => throw new RuntimeException("Unable to parse instruction stream, last instruction is missing data.") +    case _ => Unit +  } +} + +object InstructionParser { +  def apply(bytes: Seq[String]) = new InstructionParser(bytes.map(java.lang.Integer.parseInt(_, 16).toByte)) +} diff --git a/src/main.scala b/src/main.scala index f186597..4d90601 100644 --- a/src/main.scala +++ b/src/main.scala @@ -1,8 +1,6 @@  package ixee.re.disasm  object main extends App { -  def disassemble(bytes: Seq[String]): Seq[Option[Instruction]] = bytes.map(Instruction(_)) -    val bytes = """  3140 0044 1542 5c01  75f3 35d0 085a 3f40 @@ -19,7 +17,7 @@ f000 fd3f 3040 0a46  3e40 fcff 0e54 0e12  0f12 3012 7d00 b012  7a45 5f44 fcff 8f11 -""".replace("\n", " ").split(" ").filter(_ != "") +""".replace("\n", " ").replace(" ", "").trim.grouped(2).toSeq -  println(disassemble(bytes).flatten.mkString("\n")) +  println(InstructionParser(bytes).instructions.mkString("\n"))  }  | 
