summaryrefslogtreecommitdiff
path: root/src/Instruction.scala
blob: 7723ef7481cc511151effad274867ae55b0a7597 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package ixee.re.disasm

import ixee.cryptopals.utils.ByteUtils._
import ixee.cryptopals.utils.FunctionUtils._

import scala.collection.mutable.{ArrayBuffer, Map}

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

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
}

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