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})" }