summaryrefslogtreecommitdiff
path: root/src/Instruction.scala
blob: 7177b89d23332c8bc922616188ffde89022f36e7 (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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
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})"
}