From 9827904a6aa6b649665cf78fa56716011fb37e6d Mon Sep 17 00:00:00 2001 From: iximeow Date: Mon, 2 Mar 2015 15:34:34 -0800 Subject: initial commit --- .gitignore | 2 + .project | 1 + build.sbt | 9 ++ notes | 75 ++++++++++++ src/ByteUtils.scala | 114 +++++++++++++++++ src/ConversionUtils.scala | 87 +++++++++++++ src/FunctionUtils.scala | 19 +++ src/Instruction.scala | 306 ++++++++++++++++++++++++++++++++++++++++++++++ src/external/Base64.scala | 69 +++++++++++ src/main.scala | 27 ++++ src/msp430_instruction | 77 ++++++++++++ src/msp430_instruction.2 | 100 +++++++++++++++ 12 files changed, 886 insertions(+) create mode 100644 .gitignore create mode 100644 .project create mode 100644 build.sbt create mode 100644 notes create mode 100644 src/ByteUtils.scala create mode 100644 src/ConversionUtils.scala create mode 100644 src/FunctionUtils.scala create mode 100644 src/Instruction.scala create mode 100644 src/external/Base64.scala create mode 100644 src/main.scala create mode 100644 src/msp430_instruction create mode 100644 src/msp430_instruction.2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b309029 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +project/* +target/* diff --git a/.project b/.project new file mode 100644 index 0000000..a2f49b5 --- /dev/null +++ b/.project @@ -0,0 +1 @@ +project_name="msp430-disassembler" diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..2c50d75 --- /dev/null +++ b/build.sbt @@ -0,0 +1,9 @@ +name := "MPS430 disassembler" + +scalaVersion := "2.11.5" + +scalaSource in Compile := baseDirectory.value / "src" + +initialCommands in console := """ +import ixee.re.disasm._ +""" diff --git a/notes b/notes new file mode 100644 index 0000000..294e217 --- /dev/null +++ b/notes @@ -0,0 +1,75 @@ +ops: +15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +0 | 0 | 0 | 1 | 0 | 0 | opcode | bw | ad | dest | +0 | 0 | 1 | cond | pc-offset | +opcode | source | ad | bw | as | dest | + +bw = 1 for byte ops, 0 for word ops + +oneop +000 RRC 9-bit rotate through carry +001 SWPB - swap 8-bit halves. no byte form +010 RRA(.B) - arithmetic right shift +011 SXT sign extend 8 bits to 16 bits. no byte form +100 PUSH(.B) - push byte still decrements sp by 2 +101 CALL - get operand. push PC, PC = operand. no byte form. hard to do PC-relative call +110 RETI - pop SP then pop PC. opeand field unused. +111 not used + +relative jumps +all PC-relative +000 JNE/JNZ +001 JEQ/JZ +010 JNC/JLO +011 JC/JHS +100 JN +101 JGE +110 JL +111 JMP + +twoop +generally dest = src op dest +mov doesnt fetch dest, cmp/bit dont write dest. all valid in 8 bit and 16 bit + +0100 MOV - dest = src +0101 ADD - dest += src +0110 ADDC - dest += src + C +0111 SUBC - dest = dest - src + c (dest += ~src + C) +1000 SUB - dest -= src +1001 CMP - dest - src (set status only, dest not written) +1010 DADD - dest += src + C, in BCD +1011 BIT - dest & src (set status only) +1100 BIC - dest &= ~src (status not set) +1101 BIS - dest |= src (status not set) +1110 XOR - dest ^= src +1111 AND - dest &= src + +NOP implemented as virtual function, among others - +NOP == MOV r3, r3 +POP dst == MOV @SP+, dst +BR dst == MOV dst, PC +RET == MOV @SP+, PC +CLRC 1 +SETC 1 +CLRZ 2 +SETZ 2 +CLRN 4 +SETN 4 +DINT 8 +EINT 8 + +RLA dst == ADD dst, dst +RLC dst = ADDC dst, dst + +INV dst == XOR #-1, dst +CLR dst == MOV #0, dst +TST dst == CMP #0, dst + +DEC == SUB #1, dst +DECD == SUB #2, dst +INC == ADD #1, dst +INCD == ADD #2, dst + +ADC dst == ADDC #0, dst +DADC dst == DADD #0, dst +SBC dst == SUBC #0, dst diff --git a/src/ByteUtils.scala b/src/ByteUtils.scala new file mode 100644 index 0000000..723164d --- /dev/null +++ b/src/ByteUtils.scala @@ -0,0 +1,114 @@ +package ixee.cryptopals.utils + +import FunctionUtils.tup +import ConversionUtils._ + +object ByteUtils { + trait SizedNumeric[T] { + def manifest: Manifest[T] + def byteSize: Int + def bitSize: Int + def fromLong(l: Long): T + def toLong(l: T): Long + def buildFrom[U : SizedNumeric : BitOps](from: U): T = fromLong(from.toLong) + def truncate[U : SizedNumeric : BitOps](from: U): T = buildFrom(from) + override def toString = s"SizedNumeric[${manifest}]" + } + + trait BitOps[T] { + def @>>>(t: T, x: Int): T + def @<<(t: T, x: Int): T + def @|(a: T, b: T): T + def @&(a: T, b: T): T + def @^(a: T, b: T): T + def @+(a: T, b: T): T + def @-(a: T, b: T): T + } + + implicit val byteOps: BitOps[Byte] = new BitOps[Byte] { + def @>>>(t: Byte, x: Int): Byte = (t >>> x).toByte + def @<<(t: Byte, x: Int): Byte = (t << x).toByte + def @|(a: Byte, b: Byte): Byte = (a | b).toByte + def @&(a: Byte, b: Byte): Byte = (a & b).toByte + def @^(a: Byte, b: Byte): Byte = (a ^ b).toByte + def @+(a: Byte, b: Byte): Byte = (a + b).toByte + def @-(a: Byte, b: Byte): Byte = (a - b).toByte + } + + implicit val intOps: BitOps[Int] = new BitOps[Int] { + def @>>>(t: Int, x: Int): Int = t >>> x + def @<<(t: Int, x: Int): Int = (t << x) + def @|(a: Int, b: Int): Int = a | b + def @&(a: Int, b: Int): Int = a & b + def @^(a: Int, b: Int): Int = a ^ b + def @+(a: Int, b: Int): Int = a + b + def @-(a: Int, b: Int): Int = a - b + } + + implicit class WithBitOpts[T : BitOps](x: T) { + def @>>>(b: Int): T = implicitly[BitOps[T]].@>>>(x, b) + def @<<(b: Int): T = implicitly[BitOps[T]].@<<(x, b) + def @|(b: T): T = implicitly[BitOps[T]].@|(x, b) + def @&(b: T): T = implicitly[BitOps[T]].@&(x, b) + def @^(b: T): T = implicitly[BitOps[T]].@^(x, b) + def @+(b: T): T = implicitly[BitOps[T]].@+(x, b) + def @-(b: T): T = implicitly[BitOps[T]].@-(x, b) + } + + implicit class SizedWithByteInfo[T : SizedNumeric : BitOps](x: T) { + def byteSize = implicitly[SizedNumeric[T]].byteSize + def octetSize = byteSize * 2 + def bitSize = implicitly[SizedNumeric[T]].bitSize + def toLong = implicitly[SizedNumeric[T]].toLong(x) + def liftedTo[U : SizedNumeric]: U = { + val uSize = implicitly[SizedNumeric[U]] + val tSize = implicitly[SizedNumeric[T]] + if (uSize.byteSize < tSize.byteSize) { + throw new RuntimeException(s"Target size ($uSize) is smaller than the source size ($tSize)") + } + + uSize.buildFrom(x) + } + def hex: String = s"%0${octetSize}x" format x + def bitsSet: Int = { + var bits = 0 + var num = x + + while (num.toLong > 0) { + bits = bits + 1 + num = num @& (num @- 1.truncatedTo[T]) + } + + bits + } + def truncatedTo[U : SizedNumeric]: U = { + implicitly[SizedNumeric[U]].truncate(x) + } + } + + def sizedNumeric[T : Manifest](bytes: Int)(from: Long => T)(to: T => Long) = new SizedNumeric[T] { + def manifest = implicitly[Manifest[T]] + def byteSize = bytes + def bitSize = bytes * 8 + def toLong(t: T) = to(t) + def fromLong(l: Long) = from(l) + } + + implicit val intSized = sizedNumeric(4) { x: Long => x.toInt } { _.toLong } + implicit val shortized = sizedNumeric(2) { x: Long => x.toShort } { _.toLong } + implicit val byteSized = sizedNumeric(1) { x: Long => x.toByte } { _.toLong } + + implicit class SeqByteOps[T : SizedNumeric : BitOps](seq: Seq[T]) { + def xor(other: Seq[T]): Seq[T] = + seq.zip(other).map(tup(_ @^ _)) + } + + def hammingDistance(a: String, b: String): Int = + hammingDistance(a.asBytes, b.asBytes) + + def hammingDistance[T : BitOps : SizedNumeric](a: Seq[T], b: Seq[T]): Int = + (a xor b).map(_.bitsSet).reduce(_ @+ _) + + def avgHammingDistance[T : BitOps : SizedNumeric](xs: Seq[Seq[T]]): Double = + xs.sliding(2).map({ case Stream(a: Seq[T], b: Seq[T]) => hammingDistance(a, b) }).reduce(_ @+ _) / xs.length.toDouble +} diff --git a/src/ConversionUtils.scala b/src/ConversionUtils.scala new file mode 100644 index 0000000..32689fc --- /dev/null +++ b/src/ConversionUtils.scala @@ -0,0 +1,87 @@ +package ixee.cryptopals.utils + +import ByteUtils._ +import FunctionUtils._ + +object ConversionUtils { + def hexStr2Bytes(s: String): Seq[Byte] = + padToByte(s).grouped(2).map(byteStr2Byte).toStream.force + + def byteStr2Byte(str: String): Byte = + charSeq2Byte(str.toSeq) + + def charSeq2Byte(seq: Seq[Char]): Byte = + seq.map(octet2nibble).reduce(joinNibbles) + + def joinNibbles(a: Byte, b: Byte): Byte = + ((a @<< 4) @+ b) + + def padToByte(s: String): String = + if (s.length % 2 != 0) "0" + s else s + + def octet2nibble(c: Char): Byte = { + (c.toLower match { + case c if c >= 'a' && c <= 'f' => + (c - 'a') + 10.toByte + case c if c >= '0' && c <= '9' => + c - '0' + case _ => + throw new IllegalArgumentException(s"Invalid hexadecimal character: $c") + }).toByte + } + + def hexStr2Base64String(s: String): String = { + import io.github.marklister.base64.Base64._ + + hexStr2Bytes(s).toArray.toBase64 + } + + implicit class RichSeqByte(seq: Seq[Byte]) { + def to[T : SizedNumeric : BitOps]: T = { + val numeric = implicitly[SizedNumeric[T]] + if(seq.length < numeric.byteSize) { + throw new RuntimeException("Byte input is not long enough") + } + + var out: Long = seq(0) + for(i <- 1 until numeric.byteSize) { + out << 8 + out = seq(i) | out + } + numeric.fromLong(out) + } + + def hex: String = + seq.map(_.hex).reduceLeft(_ + _) + + def asAscii: String = + new String(seq.toArray) + } + + implicit class RichStringBytes(s: String) { + def asBytes = s.toSeq.map(_.toByte) + def asByteStream = s.asBytes.toStream + def asRepeatedBytes(count: Int) = + Stream.continually(s.asBytes).flatten.take(count) + } + + // TODO: tailrec this, get rid of for + def toByteSeq[T : SizedNumeric : BitOps](x: T): Seq[Byte] = { + val buf = new Array[Byte](x.byteSize) + for(i <- 0 until x.byteSize) { + val shiftAmount = (x.byteSize - i - 1) << 3 + buf(i) = (x @>>> shiftAmount).truncatedTo[Byte] + } + buf.toSeq + } + + def toBinaryString[T : SizedNumeric](x: T)(implicit a: BitOps[T]): String = { + val buf = new StringBuilder(x.bitSize) + for(i <- 0 until x.bitSize) { + val shiftAmount: Int = x.bitSize - i - 1 + buf.append((x @>>> shiftAmount) @& 0x01.liftedTo[T]) + } + buf.toString() + } +} + diff --git a/src/FunctionUtils.scala b/src/FunctionUtils.scala new file mode 100644 index 0000000..3d6610c --- /dev/null +++ b/src/FunctionUtils.scala @@ -0,0 +1,19 @@ +package ixee.cryptopals.utils + +object FunctionUtils { + // Because doing (_ f _).tupled confuses the inferencer... + def tup[A, B, C](f: (A, B) => C): ((A, B)) => C = f.tupled + + def ident[A]: A => A = { x => x } + + implicit class Compositor[A, B](f: A => B) { + def :|[C](g: B => C): A => C = f.andThen(g) + } + + implicit class Tap[A](x: A) { + def tap(f: A => Any): A = { + f(x) + x + } + } +} 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})" +} + diff --git a/src/external/Base64.scala b/src/external/Base64.scala new file mode 100644 index 0000000..692abb8 --- /dev/null +++ b/src/external/Base64.scala @@ -0,0 +1,69 @@ +package io.github.marklister.base64 + +/** + * Base64 encoder + * @author Mark Lister + * (c) Mark Lister 2014 + * + * I, the copyright holder of this work, release this work into the public domain. + * This applies worldwide. In some countries this may not be legally possible; + * if so: I grant anyone the right to use this work for any purpose, without any + * conditions, unless such conditions are required by law. + * + * The repo for this Base64 encoder lives at https://github.com/marklister/base64 + * Please send your issues, suggestions and pull requests there. + * + */ + +object Base64 { + + class B64Scheme(val encodeTable: IndexedSeq[Char]) { + lazy val decodeTable = collection.immutable.TreeMap(encodeTable.zipWithIndex: _*) + } + + lazy val base64 = new B64Scheme(('A' to 'Z') ++ ('a' to 'z') ++ ('0' to '9') ++ Seq('+', '/')) + lazy val base64Url = new B64Scheme(base64.encodeTable.dropRight(2) ++ Seq('-', '_')) + + implicit class Encoder(b: Array[Byte]) { + private[this] val zero = Array(0, 0).map(_.toByte) + lazy val pad = (3 - b.length % 3) % 3 + + def toBase64(implicit scheme: B64Scheme = base64): String = { + def sixBits(x: Array[Byte]): Seq[Int] = { + val a = (x(0) & 0xfc) >> 2 + val b = ((x(0) & 0x3) << 4) + ((x(1) & 0xf0) >> 4) + val c = ((x(1) & 0xf) << 2) + ((x(2) & 0xc0) >> 6) + val d = (x(2)) & 0x3f + Seq(a, b, c, d) + } + ((b ++ zero.take(pad)).grouped(3) + .flatMap(sixBits(_)) + .map(x => scheme.encodeTable(x)) + .toSeq + .dropRight(pad) :+ "=" * pad) + .mkString + } + } + + implicit class Decoder(s: String) { + lazy val cleanS = s.reverse.dropWhile(_ == '=').reverse + lazy val pad = s.length - cleanS.length + + def toByteArray(implicit scheme: B64Scheme = base64): Array[Byte] = { + def threeBytes(s: Seq[Char]): Array[Byte] = { + val r = s.map(scheme.decodeTable(_)).foldLeft(0)((a,b)=>(a << 6) +b) + java.nio.ByteBuffer.allocate(8).putLong(r).array().takeRight(3) + } + if (pad > 2 || s.length % 4 != 0) throw new java.lang.IllegalArgumentException("Invalid Base64 String:" + s) + if (!cleanS.forall(scheme.encodeTable.contains(_))) throw new java.lang.IllegalArgumentException("Invalid Base64 String:" + s) + + (cleanS + "A" * pad) + .grouped(4) + .map(threeBytes(_)) + .flatten + .toArray + .dropRight(pad) + } + } + +} diff --git a/src/main.scala b/src/main.scala new file mode 100644 index 0000000..9dc5d42 --- /dev/null +++ b/src/main.scala @@ -0,0 +1,27 @@ +package ixee.re.disasm + +object main extends App { + def apply: Unit = { + def disassemble(bytes: Seq[String]): Seq[Option[Instruction]] = bytes.map(Instruction(_)) + + val bytes = """ +3140 0044 1542 5c01 +75f3 35d0 085a 3f40 +0000 0f93 0724 8245 +5c01 2f83 9f4f 0c46 +0024 f923 3f40 2200 +0f93 0624 8245 5c01 +1f83 cf43 0024 fa23 +b012 2045 0f43 32d0 +f000 fd3f 3040 0a46 +3012 7f00 b012 7a45 +2153 3041 0412 0441 +2453 2183 c443 fcff +3e40 fcff 0e54 0e12 +0f12 3012 7d00 b012 +7a45 5f44 fcff 8f11 +""".replace("\n", " ").split(" ").filter(_ != "") + + println(disassemble(bytes).flatten.mkString("\n")) + } +} diff --git a/src/msp430_instruction b/src/msp430_instruction new file mode 100644 index 0000000..dfd297d --- /dev/null +++ b/src/msp430_instruction @@ -0,0 +1,77 @@ +byteOpFlag: bit{1} + +RRC := b000 +SWPB:= b001 +RRA := b010 +SXT := b011 +PUSH:= b100 +CALL:= b101 +RETI:= b110 + +oneOpCode: bit{3} := + RRC | SWPB | RRA | SXT | PUSH | CALL | RETI + +JNE := b000 +JEQ := b001 +JNC := b010 +JC := b011 +JN := b100 +JGE := b101 +JL := b110 +JMP := b111 + +jumpCondition: bit{3} := + JNE | JEQ | JNC | JC | JN | JGE | JL | JMP + +MOV := b0100 +ADD := b0101 +ADDC:= b0110 +SUBC:= b0111 +SUB := b1000 +CMP := b1001 +DADD:= b1010 +BIT := b1011 +BIC := b1100 +BIS := b1101 +XOR := b1110 +AND := b1111 + +twoOpCode: bit{4} := + MOV | ADD | ADDC | SUBC | SUB | CMP | DADD | BIT | BIC | BIS | XOR | AND + +Symbolic := b1 +Absolute := b1 +RegisterDirect := b0 +Indexed := b1 + +twoOpDestMode: bit{1} := + RegisterDirect | Indexed | Absolute | Symbolic + +NextWord := b11 +Const0 := b00 +Const1 := b01 +Const2 := b10 +Const4 := b10 +Const8 := b11 +ConstNeg1 := b11 +RegisterDirect := b00 +Indexed := b01 +RegisterIndirect := b10 +IndirectAutoInc := b11 + +twoOpSourceMode: bit{2} := + NextWord | Const0 | Const1 | + Const2 | Const4 | Const8 | + ConstNeg1 | RegisterDirect | Indexed | + RegisterIndirect | IndirectAutoInc + +oneOpDestMode: bit{2} := twoOpSourceMode + +sourceReg: bit{4} +destReg: bit{4} := sourceReg + +noOp := b001:jumpCondition:pcOffset +oneOp := b000100:oneOpCode:byteOpFlag:oneOpDestMode:destReg +twoOp := twoOpCode:sourceReg:twoOpDestMode:byteOpFlag:twoOpSourceMode:destReg + +root := oneOp | twoOp | noOp diff --git a/src/msp430_instruction.2 b/src/msp430_instruction.2 new file mode 100644 index 0000000..ccf1554 --- /dev/null +++ b/src/msp430_instruction.2 @@ -0,0 +1,100 @@ +byteOpFlag: bit{1} + +RRC := b000 +SWPB:= b001 +RRA := b010 +SXT := b011 +PUSH:= b100 +CALL:= b101 +RETI:= b110 + +oneOpCode: bit{3} := + RRC | SWPB | RRA | SXT | PUSH | CALL | RETI + +JNE := b000 +JEQ := b001 +JNC := b010 +JC := b011 +JN := b100 +JGE := b101 +JL := b110 +JMP := b111 + +jumpCondition: bit{3} := + JNE | JEQ | JNC | JC | JN | JGE | JL | JMP + +MOV := b0100 +ADD := b0101 +ADDC:= b0110 +SUBC:= b0111 +SUB := b1000 +CMP := b1001 +DADD:= b1010 +BIT := b1011 +BIC := b1100 +BIS := b1101 +XOR := b1110 +AND := b1111 + +twoOpCode: bit{4} := + MOV | ADD | ADDC | SUBC | SUB | CMP | DADD | BIT | BIC | BIS | XOR | AND + +Symbolic := b1 +Absolute := b1 +RegisterDirect := b0 +Indexed := b1 + +twoOpDestMode: bit{1} := + RegisterDirect | Indexed | Absolute | Symbolic + +NextWord := b11 +Const0 := b00 +Const1 := b01 +Const2 := b10 +Const4 := b10 +Const8 := b11 +ConstNeg1 := b11 +RegisterDirect := b00 +Indexed := b01 +RegisterIndirect := b10 +IndirectAutoInc := b11 + +twoOpSourceMode: bit{2} := + NextWord | Const0 | Const1 | + Const2 | Const4 | Const8 | + ConstNeg1 | RegisterDirect | Indexed | + RegisterIndirect | IndirectAutoInc + +oneOpDestMode: bit{2} := twoOpSourceMode + +twoOpDestModePrime := + Symbolic:b0000 | + Absolute:b0010 | + RegisterDirect:register | + Indexed:register + +# replace with register names! +twoOpSourceModePrime := + NextWord:b0000 + Const4:b0010 + Const8:b0010 + Const0:b0011 + Const1:b0011 + Const2:b0011 + ConstNeg1:b0011 + RegisterDirect:register + Indexed:register + RegisterIndirect:register + IndirectAutoInc:register + +sourceReg: bit{4} +destReg: bit{4} := sourceReg + +noOp := b001:jumpCondition:pcOffset +oneOp:= b000100:oneOpCode:byteOpFlag:oneOpDestMode:destReg +twoOp := twoOpCode:sourceReg:twoOpDestMode:byteOpFlag:twoOpSourceMode:destReg +twoOpRewrite $= twoOp => + twoOpCode:byteOpFlag:twoOpSourceMode:sourceReg:sourceReg:twoOpDestMode:destReg:destReg +twoOpPrime := twoOpCode:byteOpFlag:twoOpSourceModePrime:sourceReg:twoOpDestModePrime:destReg + +root := oneOp | twoOp | noOp -- cgit v1.1