From e250dfb67eaa802cbc03e709de49733a7651aa85 Mon Sep 17 00:00:00 2001 From: Mark Canlas Date: Sat, 5 May 2018 14:23:21 -0400 Subject: [PATCH] just checking in --- .gitignore | 4 +- build.sbt | 3 + project/build.properties | 1 + .../scala/com/htmlism/AddressingMode.scala | 15 ++ src/main/scala/com/htmlism/Instruction.scala | 95 ++++++++ src/main/scala/com/htmlism/MatchOpcodes.scala | 214 ++++++++++++++++++ .../scala/com/htmlism/ShiftExtractor.scala | 39 ++++ .../scala/com/htmlism/BitMatchingSpec.scala | 7 + 8 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 build.sbt create mode 100644 project/build.properties create mode 100644 src/main/scala/com/htmlism/AddressingMode.scala create mode 100644 src/main/scala/com/htmlism/Instruction.scala create mode 100644 src/main/scala/com/htmlism/MatchOpcodes.scala create mode 100644 src/main/scala/com/htmlism/ShiftExtractor.scala create mode 100644 src/test/scala/com/htmlism/BitMatchingSpec.scala diff --git a/.gitignore b/.gitignore index 9c07d4a..ee44a96 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -*.class -*.log +.idea +target diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..85023b5 --- /dev/null +++ b/build.sbt @@ -0,0 +1,3 @@ +initialCommands in console := "import com.htmlism._" + +libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test" \ No newline at end of file diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..9abea12 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.0.3 diff --git a/src/main/scala/com/htmlism/AddressingMode.scala b/src/main/scala/com/htmlism/AddressingMode.scala new file mode 100644 index 0000000..fb7b5db --- /dev/null +++ b/src/main/scala/com/htmlism/AddressingMode.scala @@ -0,0 +1,15 @@ +package com.htmlism + +sealed trait AddressingMode + +case object Immediate extends AddressingMode +case object ZeroPage extends AddressingMode +case object ZeroPageX extends AddressingMode +case object Absolute extends AddressingMode +case object AbsoluteX extends AddressingMode +case object AbsoluteY extends AddressingMode +case object IndirectX extends AddressingMode +case object IndirectY extends AddressingMode +case object Accumulator extends AddressingMode +case object Implied extends AddressingMode +case object NoMode extends AddressingMode \ No newline at end of file diff --git a/src/main/scala/com/htmlism/Instruction.scala b/src/main/scala/com/htmlism/Instruction.scala new file mode 100644 index 0000000..3053958 --- /dev/null +++ b/src/main/scala/com/htmlism/Instruction.scala @@ -0,0 +1,95 @@ +package com.htmlism + +sealed trait Instruction { def theme: String; def color: String } + +case object NoInstruction extends Instruction { def theme: String = "noop"; def color: String = "white" } + +sealed trait Logical extends Instruction { def theme: String = "logical"; def color: String = "OliveDrab" } + +case object AND extends Logical +case object EOR extends Logical +case object ORA extends Logical +case object BIT extends Logical + +sealed trait Arithmetic extends Instruction { def theme: String = "arithmetic"; def color: String = "CadetBlue" } + +case object ADC extends Arithmetic +case object SBC extends Arithmetic +case object CMP extends Arithmetic +case object CPX extends Arithmetic +case object CPY extends Arithmetic + +sealed trait Load extends Instruction { def theme: String = "load"; def color: String = "BurlyWood" } +case object LDA extends Load +case object LDX extends Load +case object LDY extends Load + +sealed trait Store extends Instruction { def theme: String = "store"; def color: String = "Bisque" } + +case object STA extends Store +case object STX extends Store +case object STY extends Store + +sealed trait Shift extends Instruction { def theme: String = "shift"; def color: String = "SlateBlue" } + +case object ASL extends Shift +case object LSR extends Shift +case object ROL extends Shift +case object ROR extends Shift + +sealed trait Increment extends Instruction { def theme: String = "increment"; def color: String = "LightPink" } + +case object INC extends Increment +case object INX extends Increment +case object INY extends Increment + +sealed trait Decrement extends Instruction { def theme: String = "decrement"; def color: String = "Khaki" } + +case object DEC extends Decrement +case object DEX extends Decrement +case object DEY extends Decrement + +sealed trait Jump extends Instruction { def theme: String = "jump"; def color: String = "Salmon" } +case object JMP extends Jump +case object JSR extends Jump +case object RTS extends Jump + +sealed trait Branch extends Instruction { def theme: String = "branch"; def color: String = "DodgerBlue" } +case object BCC extends Branch +case object BCS extends Branch +case object BEQ extends Branch +case object BMI extends Branch +case object BNE extends Branch +case object BPL extends Branch +case object BVC extends Branch +case object BVS extends Branch + +sealed trait System extends Instruction { def theme: String = "system"; def color: String = "Peru" } +case object BRK extends System +case object NOP extends System +case object RTI extends System + +sealed trait Stack extends Instruction { def theme: String = "stack"; def color: String = "Wheat" } +case object TSX extends Stack +case object TXS extends Stack +case object PHA extends Stack +case object PHP extends Stack +case object PLA extends Stack +case object PLP extends Stack + +sealed trait Transfer extends Instruction { def theme: String = "transfer"; def color: String = "Teal" } +case object TAX extends Transfer +case object TAY extends Transfer +case object TXA extends Transfer +case object TYA extends Transfer + +sealed trait Clear extends Instruction { def theme: String = "clear"; def color: String = "LightSteelBlue" } +case object CLC extends Clear +case object CLD extends Clear +case object CLI extends Clear +case object CLV extends Clear + +sealed trait SetFlag extends Instruction { def theme: String = "set"; def color: String = "Thistle" } +case object SEC extends SetFlag +case object SED extends SetFlag +case object SEI extends SetFlag diff --git a/src/main/scala/com/htmlism/MatchOpcodes.scala b/src/main/scala/com/htmlism/MatchOpcodes.scala new file mode 100644 index 0000000..6467a00 --- /dev/null +++ b/src/main/scala/com/htmlism/MatchOpcodes.scala @@ -0,0 +1,214 @@ +package com.htmlism + +import java.io.PrintWriter + +object MatchOpcodes { + def paddedBinary(n: Int, width: Int) = + String.format(s"%${width}s", Integer.toBinaryString(n)).replace(" ", "0") + + def main(args: Array[String]): Unit = + write(args(0))(doStuff) + + def doStuff(out: PrintWriter): Unit = { + val generated = + (0 to 255) + .map(n => n -> toOpcode(n)) + .collect { case (n, Some(x)) => (n, x) } + .toMap + + val injected = + Map( + 0x10 -> BPL, + 0x30 -> BMI, + 0x50 -> BVC, + 0x70 -> BVS, + 0x90 -> BCC, + 0xB0 -> BCS, + 0xD0 -> BNE, + 0xF0 -> BEQ, + + 0x00 -> BRK, + 0x20 -> JSR, + 0x40 -> RTI, + 0x60 -> RTS, + + 0x08 -> PHP, + 0x28 -> PLP, + 0x48 -> PHA, + 0x68 -> PLA, + 0x88 -> DEY, + 0xA8 -> TAY, + 0xC8 -> INY, + 0xE8 -> INX, + + 0x18 -> CLC, + 0x38 -> SEC, + 0x58 -> CLI, + 0x78 -> SEI, + 0x98 -> TYA, + 0xB8 -> CLV, + 0xD8 -> CLD, + 0xF8 -> SED, + + 0x8A -> TXA, + 0x9A -> TXS, + 0xAA -> TAX, + 0xBA -> TSX, + 0xCA -> DEX, + 0xEA -> NOP + ).mapValues(x => x -> Implied) + + val lookup = generated ++ injected + + out.print("") + + // print headers + out.print("") + out.print("") + + out.print("") + + + for (r <- wideRows) { + out.print("") + + // left header + out.print(s"") + + for (c <- wideColumns) { + val fullInt = (r << 5) + c + + lookup.get(fullInt) match { + case Some((ints, mode)) => + val hex = f"$fullInt%2X" + out.print(s"") + + case None => + out.print(s"") + } + } + + out.print("") + } + + out.print("
") + + for (c <- wideColumns) + out.print(s"${paddedBinary(c, 5)}
${paddedBinary(r, 3)}$ints $mode
$hex
UNDEF
") + + quartile(out, 0, lookup) + quartile(out, 1, lookup) + quartile(out, 2, lookup) + quartile(out, 3, lookup) + } + + def quartile(out: PrintWriter, n: Int, lookup: Map[Int, (Instruction, AddressingMode)]) = { + out.print(s"

${paddedBinary(n, 2)}

") + + out.print("") + + // print headers + out.print("") + out.print("") + + out.print("") + + + for (r <- rows) { + out.print("") + + // left header + out.print(s"") + + for (c <- columns) { + val fullInt = (r << (3 + 2)) + (c << 2) + n + + lookup.get(fullInt) match { + case Some((ints, mode)) => + val hex = f"$fullInt%2X" + out.print(s"") + + case None => + out.print(s"") + } + + } + + out.print("") + } + + out.print("
") + + val columns = + for { + y <- 0 to 1 + xx <- Seq(3, 1, 2, 0) + } yield (xx << 1) + y + + val rows = + 0 to 7 + + for (c <- columns) + out.print(s"${paddedBinary(c, 3)}
${paddedBinary(r, 3)}$ints $mode
$hex
UNDEF
") + } + + def wideColumns: Seq[Int] = + for { + cc <- 0 to 3 + y <- 0 to 1 + xx <- 0 to 3 + } yield (xx << 3) + (y << 2) + cc + + def wideRows: Seq[Int] = + 0 to 7 + + private def write(file: String)(f: PrintWriter => Unit) = { + val out = new PrintWriter(file) + f(out) + out.close() + } + + def toOpcode(n: Int): Option[(Instruction, AddressingMode)] = { + val BitPattern = ThreeBits >> ThreeBits >> TwoBits + + n match { + case BitPattern(aaabbb, cc) => + cc match { + case 0 => (c00 _).tupled(aaabbb) + case 1 => (c01 _).tupled(aaabbb) + case 2 => (c10 _).tupled(aaabbb) + case 3 => None + } + } + } + + def c01(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] = { + val instruction = + Seq(ORA, AND, EOR, ADC, STA, LDA, CMP, SBC)(aaa) + + val addressingMode = + Seq(IndirectX, ZeroPage, Immediate, Absolute, IndirectY, ZeroPageX, AbsoluteY, AbsoluteX)(bbb) + + Some(instruction -> addressingMode) + } + + def c10(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] = { + val instruction = + Seq(ASL, ROL, LSR, ROR, STX, LDX, DEC, INC)(aaa) + + val addressingMode = + Seq(Immediate, ZeroPage, Accumulator, Absolute, NoMode, ZeroPageX, NoMode, AbsoluteX)(bbb) + + Some(instruction -> addressingMode) + } + + def c00(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] = { + val instruction = + Seq(NoInstruction, BIT, JMP, JMP, STY, LDY, CPY, CPX)(aaa) + + val addressingMode = + Seq(Immediate, ZeroPage, NoMode, Absolute, NoMode, ZeroPageX, NoMode, AbsoluteX)(bbb) + + Some(instruction -> addressingMode) + } +} \ No newline at end of file diff --git a/src/main/scala/com/htmlism/ShiftExtractor.scala b/src/main/scala/com/htmlism/ShiftExtractor.scala new file mode 100644 index 0000000..93c08ed --- /dev/null +++ b/src/main/scala/com/htmlism/ShiftExtractor.scala @@ -0,0 +1,39 @@ +package com.htmlism + +trait BitExtractor[A] { + self => + + def length: Int + + def unapply(n: Int): Option[A] + + def >>[B](that: BitExtractor[B]): BitExtractor[(A, B)] = + new BitExtractor[(A, B)] { + def length: Int = self.length + that.length + + def unapply(n: Int): Option[(A, B)] = + for { + b <- that.unapply(n) + shifted = n >> that.length + a <- self.unapply(shifted) + } yield (a, b) + } +} + +object AtomExtractor { + def pow(ex: Int, acc: Int = 1): Int = + if (ex == 0) + acc + else + pow(ex - 1, acc * 2) +} + +abstract class PrimitiveBitExtractor(val length: Int) extends BitExtractor[Int] { + private lazy val mask = AtomExtractor.pow(length) - 1 + + def unapply(n: Int): Option[Int] = Some(n & mask) +} + +object OneBit extends PrimitiveBitExtractor(1) +object TwoBits extends PrimitiveBitExtractor(2) +object ThreeBits extends PrimitiveBitExtractor(3) \ No newline at end of file diff --git a/src/test/scala/com/htmlism/BitMatchingSpec.scala b/src/test/scala/com/htmlism/BitMatchingSpec.scala new file mode 100644 index 0000000..9c1e51a --- /dev/null +++ b/src/test/scala/com/htmlism/BitMatchingSpec.scala @@ -0,0 +1,7 @@ +package com.htmlism + +import org.scalatest.FlatSpec + +class BitMatchingSpec extends FlatSpec { + +}