package ixee.re.disasm import ixee.cryptopals.utils.ByteUtils._ import ixee.cryptopals.utils.FunctionUtils._ import scala.collection.mutable.Map 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 }