Overdue for a proper repo

This commit is contained in:
Smallhacker 2019-01-07 13:19:37 -05:00
commit 7a4b94e907
27 changed files with 2041 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.idea
/target

138
pom.xml Normal file
View File

@ -0,0 +1,138 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.smallhacker.dis-browser</groupId>
<artifactId>dis-browser</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>dis-browser</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<!-- uncomment this to get JSON support:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
</dependency>
-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.smallhacker.dis-browser.Main</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
<args>
<arg>-XXLanguage:+InlineClasses</arg>
<arg>-Xexperimental=kotlin.ExperimentalUnsignedTypes</arg>
</args>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<inherited>true</inherited>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<jersey.version>2.27</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.version>1.3.11</kotlin.version>
</properties>
</project>

View File

@ -0,0 +1,158 @@
package com.smallhacker.disbrowser
import com.smallhacker.disbrowser.asm.*
class Grid {
private val arrowCells = HashMap<Pair<Int, Int>, HtmlNode?>()
private val arrowClasses = HashMap<Pair<Int, Int>, String>()
private var arrowWidth = 0
private val content = HashMap<Pair<Int, Int>, HtmlNode?>()
private val cellClasses = HashMap<Pair<Int, Int>, String>()
private val addresses = HashMap<Address, Int>()
private val rowClasses = HashMap<Int, String>()
private val rowId = HashMap<Int, String>()
private var height = 0
private var nextAddress: Address? = null
fun arrow(from: Address, to: Address) {
val yStart = addresses[from]
val yEnd = addresses[to]
if (yStart == null || yEnd == null) {
return
}
val y1: Int
val y2: Int
val dir: String
if (yStart > yEnd) {
dir = "up"
y1 = yEnd
y2 = yStart
} else {
dir = "down"
y1 = yStart
y2 = yEnd
}
val x = nextArrowX(y1, y2)
if ((x + 1) > arrowWidth) {
arrowWidth = x + 1
}
arrowClasses[x to y1] = "arrow arrow-$dir-start"
for (y in (y1 + 1)..(y2 - 1)) {
arrowClasses[x to y] = "arrow arrow-$dir-middle"
}
arrowClasses[x to y2] = "arrow arrow-$dir-end"
//arrowCells[x to yStart] = a().addClass("arrow-link").attr("href", "#${to.toSimpleString()}")
arrowCells[x to yEnd] = div().addClass("arrow-head")
}
private fun nextArrowX(y1: Int, y2: Int): Int {
return generateSequence(0) { it + 1 }
.filter { x ->
(y1..y2).asSequence()
.map { y -> arrowClasses[x to y] }
.all { it == null }
}
.first()
}
fun add(ins: Instruction, metadata: MetadataLine?, disassembly: Disassembly) {
val address = ins.address
if (nextAddress != null) {
if (address != nextAddress) {
addDummy()
}
}
nextAddress = ins.postState.address
val y = (height++)
addresses[address] = y
add(y, ins.address,
text(ins.address.toFormattedString()),
text(ins.bytesToString()),
text(metadata?.label?.plus(":") ?: ""),
fragment {
text("${ins.opcode.mnemonic}")
text(ins.lengthSuffix)
text(" ")
val operands = ins.opcode.mode.print(ins)
val link = ins.linkedState
if (link == null) {
text(operands)
} else {
val local = link.address in disassembly
val url = when {
local -> "#${link.address.toSimpleString()}"
else -> "/${link.address.toSimpleString()}/${link.urlString}"
}
a {
text(operands)
}.attr("href", url)
}
},
text(ins.postState.toString())
)
if (ins.opcode.continuation == Continuation.NO) {
rowClasses[y] = "routine-end"
}
}
private fun addDummy() {
val y = (height++)
add(y, null, null, null, null, text("..."), null)
}
private fun add(y: Int, address: Address?,
cAddress: HtmlNode?,
cBytes: HtmlNode?,
cLabel: HtmlNode?,
cCode: HtmlNode?,
cState: HtmlNode?
) {
if (address != null) {
rowId[y] = address.toSimpleString()
}
content[0 to y] = cAddress
content[1 to y] = cBytes
content[2 to y] = cLabel
content[3 to y] = cCode
content[4 to y] = cState
}
fun output(): HtmlNode {
return table {
for (y in 0 until height) {
tr {
for (x in 0..3) {
val cssClass = cellClasses[x to y]
td {
content[x to y]?.appendTo(parent)
}.addClass(cssClass)
}
for (x in 0 until arrowWidth) {
val cssClass = arrowClasses[x to y]
td {
arrowCells[x to y]?.appendTo(parent)
}.addClass(cssClass)
}
for (x in 4..4) {
val cssClass = cellClasses[x to y]
td {
content[x to y]?.appendTo(parent)
}.addClass(cssClass)
}
}.addClass(rowClasses[y]).attr("id", rowId[y])
}
}
}
}

View File

@ -0,0 +1,126 @@
package com.smallhacker.disbrowser
interface HtmlNode {
fun print(): String {
val out = StringBuilder()
printTo(out)
return out.toString()
}
fun printTo(out: StringBuilder): StringBuilder
fun attr(key: String): String? = null
fun attr(key: String, value: String?): HtmlNode = this
fun append(node: HtmlNode): HtmlNode = this
}
open class HtmlElement(protected val tag: String) : HtmlNode {
private val attributes = LinkedHashMap<String, String>()
final override fun toString(): String = print()
override fun printTo(out: StringBuilder): StringBuilder {
out.append("<", tag)
attributes.forEach { key, value ->
out.append(" ", key, "=\"", value, "\"")
}
return out.append(">")
}
final override fun attr(key: String): String? = attributes[key]
final override fun attr(key: String, value: String?) = apply {
if (value != null) {
attributes[key] = value
} else {
attributes.remove("key")
}
}
}
private class ParentHtmlElement(tag: String, inner: InnerHtml) : HtmlElement(tag) {
private val children = ArrayList<HtmlNode>()
init {
inner(HtmlArea(this))
}
override fun printTo(out: StringBuilder): StringBuilder {
super.printTo(out)
children.forEach { it.printTo(out) }
out.append("</", tag, ">")
return out
}
override fun append(node: HtmlNode) = apply { children.add(node) }
}
private fun parent(tag: String, inner: InnerHtml): HtmlNode = ParentHtmlElement(tag, inner)
private fun leaf(tag: String): HtmlNode = HtmlElement(tag)
typealias InnerHtml = HtmlArea.() -> Unit
class HtmlArea(val parent: HtmlNode)
fun text(text: String): HtmlNode = object : HtmlNode {
override fun printTo(out: StringBuilder) = out.append(text)
override fun attr(key: String, value: String?) = this
override fun append(node: HtmlNode): HtmlNode = this
}
fun HtmlArea.text(text: String) = com.smallhacker.disbrowser.text(text).appendTo(parent)
fun fragment(inner: InnerHtml = {}) = object : HtmlNode {
private val children = ArrayList<HtmlNode>()
init {
inner(HtmlArea(this))
}
override fun printTo(out: StringBuilder) = out.apply { children.forEach { it.printTo(out) } }
override fun append(node: HtmlNode) = apply { children.add(node) }
}
fun HtmlArea.fragment(inner: InnerHtml = {}) = com.smallhacker.disbrowser.fragment(inner).appendTo(parent)
fun html(inner: InnerHtml = {}) = parent("html", inner)
fun HtmlArea.html(inner: InnerHtml = {}) = com.smallhacker.disbrowser.html(inner).appendTo(parent)
fun head(inner: InnerHtml = {}) = parent("head", inner)
fun HtmlArea.head(inner: InnerHtml = {}) = com.smallhacker.disbrowser.head(inner).appendTo(parent)
fun title(inner: InnerHtml = {}) = parent("title", inner)
fun HtmlArea.title(inner: InnerHtml = {}) = com.smallhacker.disbrowser.title(inner).appendTo(parent)
fun link(inner: InnerHtml = {}) = leaf("link")
fun HtmlArea.link(inner: InnerHtml = {}) = com.smallhacker.disbrowser.link(inner).appendTo(parent)
fun body(inner: InnerHtml = {}) = parent("body", inner)
fun HtmlArea.body(inner: InnerHtml = {}) = com.smallhacker.disbrowser.body(inner).appendTo(parent)
fun div(inner: InnerHtml = {}) = parent("div", inner)
fun HtmlArea.div(inner: InnerHtml = {}) = com.smallhacker.disbrowser.div(inner).appendTo(parent)
fun table(inner: InnerHtml = {}) = parent("table", inner)
fun HtmlArea.table(inner: InnerHtml = {}) = com.smallhacker.disbrowser.table(inner).appendTo(parent)
fun tr(inner: InnerHtml = {}) = parent("tr", inner)
fun HtmlArea.tr(inner: InnerHtml = {}) = com.smallhacker.disbrowser.tr(inner).appendTo(parent)
fun td(inner: InnerHtml = {}) = parent("td", inner)
fun HtmlArea.td(inner: InnerHtml = {}) = com.smallhacker.disbrowser.td(inner).appendTo(parent)
fun a(inner: InnerHtml = {}) = parent("a", inner)
fun HtmlArea.a(inner: InnerHtml = {}) = com.smallhacker.disbrowser.a(inner).appendTo(parent)
fun script(inner: InnerHtml = {}) = parent("script", inner)
fun HtmlArea.script(inner: InnerHtml = {}) = com.smallhacker.disbrowser.script(inner).appendTo(parent)
fun HtmlNode.appendTo(node: HtmlNode) = apply { node.append(this) }
fun HtmlNode.addClass(c: String?) = attrAdd("class", c)
fun HtmlNode.attrSet(name: String): MutableSet<String> = (attr(name) ?: "")
.split(" ")
.asSequence()
.filterNot { it.isEmpty() }
.toMutableSet()
fun HtmlNode.attrAdd(name: String, value: String?) = apply {
if (value != null) {
val set = attrSet(name)
set.add(value)
attr(name, set.joinToString(" "))
}
}

View File

@ -0,0 +1,39 @@
//package com.smallhacker.disbrowser
//
//class HtmlContext(val out: StringBuilder)
//
//fun html(inner: HtmlContext.() -> Unit = {}): String {
// val html = HtmlContext(StringBuilder().append("<!DOCTYPE html>"))
// element(html, "html") { inner(html) }
// return html.out.toString()
//}
//
//private fun element(html: HtmlContext, tag: String, inner: HtmlContext.() -> Unit) = element(html, tag, inner, *emptyArray())
//
//private fun element(html: HtmlContext, tag: String, inner: HtmlContext.() -> Unit, vararg args: String) {
// html.out.append("<$tag")
// html.out.append(args.asSequence().map { " $it" }.joinToString())
// html.out.append(">")
// html.inner()
// html.out.append("</$tag>")
//}
//
//
//fun HtmlContext.text(text: String) {
// this.out.append(text)
//}
//
//fun HtmlContext.head(inner: HtmlContext.() -> Unit = {}) = element(this, "head", inner)
//fun HtmlContext.title(inner: HtmlContext.() -> Unit = {}) = element(this, "title", inner)
//fun HtmlContext.body(inner: HtmlContext.() -> Unit = {}) = element(this, "body", inner)
//fun HtmlContext.style(inner: HtmlContext.() -> Unit = {}) = element(this, "style", inner)
//fun HtmlContext.link(href: String, inner: HtmlContext.() -> Unit = {}) = element(this, "link", inner, "rel=\"stylesheet\" href=\"$href\"")
//fun HtmlContext.div(inner: HtmlContext.() -> Unit = {}) = element(this, "div", inner)
//fun HtmlContext.div(cssClass: String, inner: HtmlContext.() -> Unit = {}) = element(this, "div", inner, "class=\"$cssClass\"")
//
//fun HtmlContext.table(inner: HtmlContext.() -> Unit = {}) = element(this, "table", inner)
//fun HtmlContext.tr(inner: HtmlContext.() -> Unit = {}) = element(this, "tr", inner)
//fun HtmlContext.tr(cssClass: String?, inner: HtmlContext.() -> Unit = {}) = element(this, "tr", inner, (if (cssClass == null) "" else "class=\"$cssClass\""))
//fun HtmlContext.td(inner: HtmlContext.() -> Unit = {}) = element(this, "td", inner)
//fun HtmlContext.td(cssClass: String?, inner: HtmlContext.() -> Unit = {}) = element(this, "td", inner, (if (cssClass == null) "" else "class=\"$cssClass\""))
//fun HtmlContext.a(href: String, inner: HtmlContext.() -> Unit = {}) = element(this, "a", inner, "href=\"$href\"")

View File

@ -0,0 +1,37 @@
package com.smallhacker.disbrowser
interface ImmStack<E>: Iterable<E> {
fun isEmpty(): Boolean
val top: E?
fun pop(): ImmStack<E>
fun push(value: E): ImmStack<E> = ImmStackImpl(this, value)
}
fun <T> immStack(): ImmStack<T> {
@Suppress("UNCHECKED_CAST")
return EmptyImmStack as ImmStack<T>
}
private class ImmStackImpl<E>(private val parent: ImmStack<E>, override val top: E): ImmStack<E> {
override fun isEmpty() = false
override fun pop(): ImmStack<E> = parent
override fun iterator(): Iterator<E> {
return sequenceOf(top).plus(parent).iterator()
}
}
private object EmptyImmStack: ImmStack<Any?> {
override fun isEmpty() = true
override val top: Any? = null
override fun pop() = this
override fun iterator() = emptySequence<Any?>().iterator()
}

View File

@ -0,0 +1,45 @@
package com.smallhacker.disbrowser;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import java.io.IOException;
import java.net.URI;
/**
* Main class.
*
*/
public class Main {
// Base URI the Grizzly HTTP server will listen on
public static final String BASE_URI = "http://localhost:8080/";
/**
* Starts Grizzly HTTP server exposing JAX-RS resources defined in this application.
* @return Grizzly HTTP server.
*/
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
// in com.smallhacker.disbrowser package
final ResourceConfig rc = new ResourceConfig().packages("com.smallhacker.disbrowser");
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
/**
* Main method.
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
System.in.read();
server.stop();
}
}

View File

@ -0,0 +1,159 @@
package com.smallhacker.disbrowser
import com.smallhacker.disbrowser.asm.*
import com.smallhacker.disbrowser.disassembler.Disassembler
import com.smallhacker.disbrowser.util.tryParseInt
import java.nio.file.Paths
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response
private val RESET_VECTOR_LOCATION = Address(0x00_FFFC)
@Path("/")
class MyResource {
private val romData = lazy {
//RomData.load(Paths.get("P:\\Emulation\\ROMs\\SNES\\Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"))
RomData.load(Paths.get("""P:\Emulation\ROMs\SNES\Super Mario World.sfc"""))
}
@GET
fun getIt(): Response {
return handle {
val resetVectorLocation = RESET_VECTOR_LOCATION
val initialAddress = Address(romData.value.getWord(resetVectorLocation.pc).value)
val flags = parseState("MX")
showDisassembly(initialAddress, flags)
}
}
@GET
@Path("{address}")
fun getIt(@PathParam("address") address: String): Response {
return handle {
parseAddress(address)?.let {
val flags = parseState("")
showDisassembly(it, flags)
}
}
}
@GET
@Path("{address}/{state}")
@Produces(MediaType.TEXT_HTML)
fun getIt(@PathParam("address") address: String, @PathParam("state") state: String): Response {
return handle {
parseAddress(address)?.let {
val flags = parseState(state)
showDisassembly(it, flags)
}
}
}
private fun parseAddress(address: String): Address? {
return tryParseInt(address, 16)
?.let { Address(it) }
}
private fun handle(runner: () -> HtmlNode?): Response {
try {
val disassembly = runner()
return if (disassembly == null)
Response.status(404).build()
else
Response.ok(disassembly.toString(), MediaType.TEXT_HTML).build()
} catch (e: Exception) {
e.printStackTrace()
throw e
}
}
private fun showDisassembly(initialAddress: Address, flags: VagueNumber): HtmlNode? {
val rom = romData.value
val metadata = getMetadata()
val initialState = State(data = rom, address = initialAddress, flags = flags)
val disassembly = Disassembler.disassemble(initialState, metadata, false)
return print(disassembly, metadata)
}
private fun parseState(state: String): VagueNumber {
var flags = VagueNumber()
flags = parseFlag(state, flags, 'M', 'm', 0x20)
flags = parseFlag(state, flags, 'X', 'x', 0x10)
return flags
}
@GET
@Path("resources/{file}.{ext}")
fun getCss(@PathParam("file") file: String, @PathParam("ext") ext: String): Response {
val mime = when (ext) {
"js" -> "application/javascript"
"css" -> "text/css"
else -> null
}
if (mime != null) {
javaClass.getResourceAsStream("/$file.$ext")
?.bufferedReader()
?.use {
return Response.ok(it.readText())
.type(mime)
.build()
}
}
return Response.status(404).build()
}
private fun parseFlag(state: String, flags: VagueNumber, set: Char, clear: Char, mask: Int): VagueNumber = when {
state.contains(set) -> flags.withBits(mask)
state.contains(clear) -> flags.withoutBits(mask)
else -> flags
}
private fun print(disassembly: Disassembly, metadata: Metadata): HtmlNode {
val grid = Grid()
disassembly.forEach { grid.add(it, metadata[it.address], disassembly) }
disassembly.asSequence()
.mapNotNull {
it.linkedState
?.let { link ->
it.address to link.address
}
}
.sortedBy { Math.abs(it.first.value - it.second.value) }
.forEach { grid.arrow(it.first, it.second) }
return html {
head {
title { text("Disassembly Browser") }
link {}.attr("rel", "stylesheet").attr("href", "/resources/style.css")
}
body {
grid.output().appendTo(parent)
script().attr("src", "/resources/main.js")
}
}
}
private fun getMetadata(): Metadata {
return metadata {
at(0x00_8000) { label = "RESET_VECTOR" }
at(0x00_80c6) { flags.add(JmpIndirectLongInterleavedTable(Address(0x00_8061), 28)) }
at(0x00_879c) { flags.add(NonReturningRoutine) }
at(0x02_87d0) { flags.add(JslTableRoutine(4)) }
at(0x0c_c115) { flags.add(JslTableRoutine(12)) }
//at(0x0c_c115) { stop = true }
}
}
}

View File

@ -0,0 +1,19 @@
package com.smallhacker.disbrowser.asm
data class Address(val value: Int): Comparable<Address> {
val rom = (value and 0x8000) == 0
val pc = (value and 0x7FFF) or ((value and 0x7F_0000) shr 1)
operator fun plus(offset: Int) = Address(value + offset)
operator fun minus(offset: Int) = Address(value - offset)
operator fun inc() = plus(1)
operator fun dec() = plus(1)
override fun toString(): String = toFormattedString()
fun toFormattedString(): String = String.format("$%02x:%04x", value shr 16, value and 0xFFFF)
fun toSimpleString(): String = String.format("%06x", value)
fun withinBank(value: Int): Address = Address((this.value and 0xFF_0000) or value)
override fun compareTo(other: Address) = value.compareTo(other.value)
}

View File

@ -0,0 +1,5 @@
package com.smallhacker.disbrowser.asm
enum class Continuation {
NO, YES, MAYBE
}

View File

@ -0,0 +1,13 @@
package com.smallhacker.disbrowser.asm
class Disassembly(lines: List<Instruction>) : Iterable<Instruction> {
override fun iterator() = lines.values.iterator() as Iterator<Instruction>
private val lines = LinkedHashMap<Address, Instruction>()
init {
lines.forEach { this.lines[it.address] = it }
}
operator fun contains(address: Address) = address in lines
}

View File

@ -0,0 +1,106 @@
package com.smallhacker.disbrowser.asm
import com.smallhacker.disbrowser.util.*
class Instruction(val bytes: RomData, val opcode: Opcode, val preState: State) {
val address: Address
get() = preState.address
val postState = opcode.mutate(this)
.mutateAddress { it + bytes.size }
.withOrigin(this)
val linkedState = link()?.let { link ->
opcode.mutate(this)
.mutateAddress { link }
.withOrigin(this)
}
operator fun get(index: Int): UByte {
return bytes[index]
}
val signedByte: Int
get() = byte.value.toByte().toInt()
val signedWord: Int
get() = word.value.toShort().toInt()
val byte: UByte
get() = get(1)
val byte2: UByte
get() = get(2)
val dataByte: UByte
get() = get(0)
val word: UWord
get() = (get(2).toWord() left 8) or get(1).toWord()
val dataWord: UWord
get() = (get(1).toWord() left 8) or get(0).toWord()
val long: ULong
get() = (get(3).toLong() left 16) or (get(2).toLong() left 8) or get(1).toLong()
val dataLong: ULong
get() = (get(2).toLong() left 16) or (get(1).toLong() left 8) or get(0).toLong()
fun bytesToString(): String {
return bytes.asSequence().map { it.toHex() }.joinToString(" ").padEnd(11, ' ')
}
//val value: ULong
// get() {
// val len = operandLength
// val start = opcode.operandIndex
//}
//fun getOperand(index: Int, length: Int) {
// val v = uLong(0)
// for (i in (index + length) downTo index)
//}
val lengthSuffix: String
get() {
return when (opcode.mode) {
Mode.IMPLIED -> ""
Mode.IMMEDIATE_8 -> ""
Mode.IMMEDIATE_16 -> ""
Mode.RELATIVE -> ""
Mode.RELATIVE_LONG -> ""
Mode.BLOCK_MOVE -> ""
else -> when (operandLength) {
null -> ".?"
1 -> ".b"
2 -> ".w"
3 -> ".l"
else -> ""
}
}
}
private val operandLength
get() = opcode.mode.operandLength(preState)
private fun link(): Address? {
if (!opcode.link) {
return null
}
return when (opcode.mode) {
Mode.ABSOLUTE -> address.withinBank(word.value)
Mode.ABSOLUTE_LONG -> Address(long.value)
Mode.RELATIVE -> address + 2 + signedByte
Mode.RELATIVE_LONG -> address + 3 + signedWord
Mode.DATA_WORD -> address.withinBank(dataWord.value)
Mode.DATA_LONG -> Address(dataLong.value)
else -> null
}
}
override fun toString(): String {
return "$address ${bytesToString()} ${opcode.mnemonic} ${opcode.mode.print(this).padEnd(100, ' ')} ($preState -> $postState)"
}
}

View File

@ -0,0 +1,66 @@
package com.smallhacker.disbrowser.asm
import com.smallhacker.disbrowser.util.left
import com.smallhacker.disbrowser.util.or
import com.smallhacker.disbrowser.util.toLong
class Metadata {
private val lines = HashMap<Address, MetadataLine>()
fun add(line: MetadataLine): Metadata {
lines[line.address] = line
return this
}
operator fun get(address: Address): MetadataLine? {
return lines[address]
}
}
fun Metadata.at(address: Int, runner: MetadataLine.() -> Unit) {
val line = MetadataLine(Address(address))
this.add(line)
runner(line)
}
fun metadata(runner: Metadata.() -> Unit): Metadata {
val metadata = Metadata()
runner(metadata)
return metadata
}
interface InstructionFlag
object NonReturningRoutine : InstructionFlag
class JmpIndirectLongInterleavedTable(private val start: Address, private val entries: Int) : InstructionFlag {
fun readTable(data: RomData): Sequence<Address> {
return (0 until entries)
.asSequence()
.map { it + start.pc }
.map { pc -> data[pc].toLong() or (data[pc + entries].toLong() left 8) or (data[pc + entries + entries].toLong() left 16) }
.map { Address(it.value) }
}
}
class JslTableRoutine(private val entries: Int) : InstructionFlag {
fun readTable(postJsr: State): Sequence<Address> {
val data = postJsr.data
return (0 until entries)
.asSequence()
.map { postJsr.address.pc + (it * 3) }
.map { pc -> data[pc].toLong() or (data[pc + 1].toLong() left 8) or (data[pc + 2].toLong() left 16) }
.map { Address(it.value) }
}
// fun dataInstructions(postJsr: State) {
// val data = postJsr.data
// return (0 until entries)
// .asSequence()
// .map {
// val offset = it * 3
// Instruction(data.range(postJsr.address.pc + offset, 3), Opcode.POINTER_LONG, postJsr.mutateAddress { it + offset })
// }
// }
}

View File

@ -0,0 +1,8 @@
package com.smallhacker.disbrowser.asm
data class MetadataLine(
val address: Address,
var label: String? = null,
var comment: String? = null,
val flags: MutableList<InstructionFlag> = ArrayList()
)

View File

@ -0,0 +1,15 @@
package com.smallhacker.disbrowser.asm
enum class Mnemonic {
ADC, AND, ASL, BCC, BCS, BEQ, BIT, BMI, BNE, BPL, BRA,
BRK, BRL, BVC, BVS, CLC, CLD, CLI, CLV, CMP, COP, CPX,
CPY, DEC, DEX, DEY, EOR, INC, INX, INY, JMP, JML, JSL,
JSR, LDA, LDX, LDY, LSR, MVN, MVP, NOP, ORA, PEA, PEI,
PER, PHA, PHB, PHD, PHK, PHP, PHX, PHY, PLA, PLB, PLD,
PLP, PLX, PLY, REP, ROL, ROR, RTI, RTL, RTS, SBC, SEC,
SED, SEI, SEP, STA, STP, STX, STY, STZ, TAX, TAY, TCD,
TCS, TDC, TRB, TSB, TSC, TSX, TXA, TXS, TXY, TYA, TYX,
WAI, WDM, XBA, XCE,
DB, DW, DL
}

View File

@ -0,0 +1,111 @@
package com.smallhacker.disbrowser.asm
import com.smallhacker.disbrowser.util.*
fun format(format: String, value: UVal<*>): String {
return format.replace(Regex("[0]+"), value.toHex())
}
enum class Mode {
DATA_BYTE(1, "$00", { dataByte }, dataMode = true),
DATA_WORD(2, "$0000", { dataWord }, dataMode = true),
DATA_LONG(3, "$000000", { dataLong }, dataMode = true),
IMPLIED(1, { "" }),
IMMEDIATE_8(2, "#$00", { byte }),
IMMEDIATE_16(3, "#$0000", { word }),
IMMEDIATE_M(-1, {
when (preState.m) {
null -> "????"
true -> format("#$00", byte)
false -> format("#$0000", word)
}
}),
IMMEDIATE_X(-2, {
when (preState.x) {
null -> "???"
true -> format("#$00", byte)
false -> format("#$0000", word)
}
}),
ABSOLUTE(3, "$0000", { word }),
ABSOLUTE_X(3, "$0000,x", { word }),
ABSOLUTE_Y(3, "$0000,y", { word }),
ABSOLUTE_LONG(4, "$000000", { long }),
ABSOLUTE_LONG_X(4, "$000000,x", { long }),
ABSOLUTE_INDIRECT(3, "($0000)", { word }),
ABSOLUTE_INDIRECT_LONG(3, "[$0000]", { word }),
ABSOLUTE_X_INDIRECT(3, "($0000,x)", { word }),
DIRECT(2, "$00", { byte }),
DIRECT_X(2, "$00,x", { byte }),
DIRECT_Y(2, "$00,y", { byte }),
DIRECT_S(2, "$00,s", { byte }),
DIRECT_INDIRECT(2, "($00)", { byte }),
DIRECT_INDIRECT_Y(2, "($00),y", { byte }),
DIRECT_X_INDIRECT(2, "($00,x)", { byte }),
DIRECT_S_INDIRECT_Y(2, "($00,s),y", { byte }),
DIRECT_INDIRECT_LONG(2, "[$00]", { byte }),
DIRECT_INDIRECT_LONG_Y(2, "[$00],y", { byte }),
RELATIVE(2, {
val rel = signedByte.toInt() + 2
format("$000000", uLong((address + rel).value))
}),
//RELATIVE_LONG(3, "$0000", { word }),
RELATIVE_LONG(3, {
val rel = signedWord.toInt() + 3
format("$000000", uLong((address + rel).value))
}),
BLOCK_MOVE(3, { String.format("#$%02x,#$%02x", byte.value, byte2.value) })
;
private val length: Int
val print: Instruction.() -> String
val dataMode: Boolean
constructor(length: Int, print: Instruction.() -> String) {
this.length = length
this.print = print
this.dataMode = false
}
constructor(length: Int, format: String, valueGetter: Instruction.() -> UVal<*>, dataMode: Boolean = false) {
this.length = length
this.print = { format(format, valueGetter(this)) }
this.dataMode = dataMode
}
/**
* Returns the total length, in bytes, of an instruction of this mode and its operands.
*
* This is usually one greater than [operandLength], except in the cases when the instruction is just pure data
* without an opcode (in which case the two are equal).
*
* If the length cannot be determined based on the current [State], `null` is returned.
*/
fun instructionLength(state: State): Int? {
return when (length) {
-1 -> state.mWidth?.plus(1)
-2 -> state.xWidth?.plus(1)
else -> length
}
}
/**
* Returns the length, in bytes, of the operands of an instruction of this mode.
*
* This is usually one less than [operandLength], except in the cases when the instruction is just pure data
* without an opcode (in which case the two are equal).
*
* If the length cannot be determined based on the current [State], `null` is returned.
*/
fun operandLength(state: State): Int? {
val len = instructionLength(state) ?: return null
return when(this) {
DATA_BYTE,
DATA_WORD,
DATA_LONG -> len
else -> len - 1
}
}
}

View File

@ -0,0 +1,19 @@
package com.smallhacker.disbrowser.asm
abstract class ModeFormat {
abstract fun print(instruction: Instruction, metadata: Metadata): String
//fun wrap(prefix: String = "", suffix: String = ""): ModeFormat {
//
//}
}
//private class AddressModeFormat: ModeFormat() {
// override fun print(instruction: Instruction, metadata: Metadata): String {
// val mode = instruction.opcode.mode
// val operandLength = mode.operandLength(instruction.preState)
// instruction.
//
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
// }
//}

View File

@ -0,0 +1,363 @@
package com.smallhacker.disbrowser.asm
import java.util.HashMap
import com.smallhacker.disbrowser.asm.Mnemonic.*
import com.smallhacker.disbrowser.asm.Mode.*
import com.smallhacker.disbrowser.util.UByte
typealias SegmentEnder = Instruction.() -> SegmentEnd?
class Opcode private constructor(val mnemonic: Mnemonic, val mode: Mode, val ender: SegmentEnder, val mutate: (Instruction) -> State) {
private var _continuation = Continuation.YES
private var _link = false
private var _branch = false
val operandIndex
get() = if (mode.dataMode) 0 else 1
val continuation: Continuation
get() = _continuation
val link: Boolean
get() = _link
val branch: Boolean
get() = _branch
private fun stop(): Opcode {
this._continuation = Continuation.NO
return this
}
private fun mayStop(): Opcode {
this._continuation = Continuation.MAYBE
return this
}
private fun linking(): Opcode {
this._link = true
return this
}
private fun branching(): Opcode {
linking()
this._branch = true
return this
}
companion object {
val DATA_BYTE = Opcode(Mnemonic.DB, Mode.DATA_BYTE, { null }, { it.preState })
val DATA_WORD = Opcode(Mnemonic.DW, Mode.DATA_WORD, { null }, { it.preState })
val DATA_LONG = Opcode(Mnemonic.DL, Mode.DATA_LONG, { null }, { it.preState })
val POINTER_WORD = Opcode(Mnemonic.DW, Mode.DATA_WORD, { null }, { it.preState }).linking()
val POINTER_LONG = Opcode(Mnemonic.DL, Mode.DATA_LONG, { null }, { it.preState }).linking()
private val OPCODES: Array<Opcode>
fun opcode(byteValue: UByte): Opcode {
return OPCODES[byteValue.value]
}
init {
val ocs = HashMap<Int, Opcode>()
fun add(value: Int, mnemonic: Mnemonic, mode: Mode, ender: SegmentEnder, mutate: (Instruction) -> State = { it.preState }): Opcode {
val opcode = Opcode(mnemonic, mode, ender, mutate)
ocs[value] = opcode
return opcode
}
val alwaysContinue: SegmentEnder = { null }
val alwaysStop: SegmentEnder = { stoppingSegmentEnd(address) }
val branching: SegmentEnder = { branchingSegmentEnd(address, postState, linkedState!!) }
val alwaysBranching: SegmentEnder = { alwaysBranchingSegmentEnd(address, linkedState!!) }
val jumping: SegmentEnder = { jumpSegmentEnd(address, linkedState!!) }
val dynamicJumping: SegmentEnder = { stoppingSegmentEnd(address) }
val subJumping: SegmentEnder = { subroutineSegmentEnd(address, linkedState!!, postState.address) }
val dynamicSubJumping: SegmentEnder = { stoppingSegmentEnd(address) }
val returning: SegmentEnder = { returnSegmentEnd(address) }
add(0x00, BRK, IMMEDIATE_8, alwaysStop).stop()
add(0x02, COP, IMMEDIATE_8, alwaysStop).stop()
add(0x42, WDM, IMMEDIATE_8, alwaysStop).stop()
add(0xEA, NOP, IMPLIED, alwaysContinue)
add(0xDB, STP, IMPLIED, alwaysStop).stop()
add(0xCB, WAI, IMPLIED, alwaysContinue)
add(0x10, BPL, RELATIVE, branching).branching()
add(0x30, BMI, RELATIVE, branching).branching()
add(0x50, BVC, RELATIVE, branching).branching()
add(0x70, BVS, RELATIVE, branching).branching()
add(0x80, BRA, RELATIVE, alwaysBranching).stop().branching()
add(0x90, BCC, RELATIVE, branching).branching()
add(0xB0, BCS, RELATIVE, branching).branching()
add(0xD0, BNE, RELATIVE, branching).branching()
add(0xF0, BEQ, RELATIVE, branching).branching()
add(0x82, BRL, RELATIVE_LONG, alwaysBranching).stop().branching()
add(0x4C, JMP, ABSOLUTE, jumping).linking().stop()
add(0x5C, JML, ABSOLUTE_LONG, jumping).linking().stop()
add(0x6C, JMP, ABSOLUTE_INDIRECT, dynamicJumping).stop()
add(0x7C, JMP, ABSOLUTE_X_INDIRECT, dynamicJumping).stop()
add(0xDC, JMP, ABSOLUTE_INDIRECT_LONG, dynamicJumping).stop()
add(0x22, JSL, ABSOLUTE_LONG, subJumping).linking().mayStop()
add(0x20, JSR, ABSOLUTE, subJumping).linking().mayStop()
add(0xFC, JSR, ABSOLUTE_X_INDIRECT, dynamicSubJumping).mayStop()
add(0x60, RTS, IMPLIED, returning).stop()
add(0x6B, RTL, IMPLIED, returning).stop()
add(0x40, RTI, IMPLIED, returning).stop()
add(0x1B, TCS, IMPLIED, alwaysContinue)
add(0x3B, TSC, IMPLIED, alwaysContinue)
add(0x5B, TCD, IMPLIED, alwaysContinue)
add(0x7B, TDC, IMPLIED, alwaysContinue)
add(0xAA, TAX, IMPLIED, alwaysContinue)
add(0xA8, TAY, IMPLIED, alwaysContinue)
add(0xBA, TSX, IMPLIED, alwaysContinue)
add(0x8A, TXA, IMPLIED, alwaysContinue)
add(0x9A, TXS, IMPLIED, alwaysContinue)
add(0x9B, TXY, IMPLIED, alwaysContinue)
add(0x98, TYA, IMPLIED, alwaysContinue)
add(0xBB, TYX, IMPLIED, alwaysContinue)
add(0xEB, XBA, IMPLIED, alwaysContinue)
add(0x18, CLC, IMPLIED, alwaysContinue)
add(0x38, SEC, IMPLIED, alwaysContinue)
add(0x58, CLI, IMPLIED, alwaysContinue)
add(0x78, SEI, IMPLIED, alwaysContinue)
add(0xF8, SED, IMPLIED, alwaysContinue)
add(0xD8, CLD, IMPLIED, alwaysContinue)
add(0xB8, CLV, IMPLIED, alwaysContinue)
add(0xE2, SEP, IMMEDIATE_8, alwaysContinue) { it.preState.sep(it.bytes[1]) }
add(0xC2, REP, IMMEDIATE_8, alwaysContinue) { it.preState.rep(it.bytes[1]) }
add(0xFB, XCE, IMPLIED, alwaysContinue)
add(0xC1, CMP, DIRECT_X_INDIRECT, alwaysContinue)
add(0xC3, CMP, DIRECT_S, alwaysContinue)
add(0xC5, CMP, DIRECT, alwaysContinue)
add(0xC7, CMP, DIRECT_INDIRECT_LONG, alwaysContinue)
add(0xC9, CMP, IMMEDIATE_M, alwaysContinue)
add(0xCD, CMP, ABSOLUTE, alwaysContinue)
add(0xCF, CMP, ABSOLUTE_LONG, alwaysContinue)
add(0xD1, CMP, DIRECT_INDIRECT_Y, alwaysContinue)
add(0xD2, CMP, DIRECT_INDIRECT, alwaysContinue)
add(0xD3, CMP, DIRECT_S_INDIRECT_Y, alwaysContinue)
add(0xD5, CMP, DIRECT_X, alwaysContinue)
add(0xD7, CMP, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
add(0xD9, CMP, ABSOLUTE_Y, alwaysContinue)
add(0xDD, CMP, ABSOLUTE_X, alwaysContinue)
add(0xDF, CMP, ABSOLUTE_LONG_X, alwaysContinue)
add(0xE0, CPX, IMMEDIATE_X, alwaysContinue)
add(0xE4, CPX, DIRECT, alwaysContinue)
add(0xEC, CPX, ABSOLUTE, alwaysContinue)
add(0xC0, CPY, IMMEDIATE_X, alwaysContinue)
add(0xC4, CPY, DIRECT, alwaysContinue)
add(0xCC, CPY, ABSOLUTE, alwaysContinue)
add(0xA1, LDA, DIRECT_X_INDIRECT, alwaysContinue)
add(0xA3, LDA, DIRECT_S, alwaysContinue)
add(0xA5, LDA, DIRECT, alwaysContinue)
add(0xA7, LDA, DIRECT_INDIRECT_LONG, alwaysContinue)
add(0xA9, LDA, IMMEDIATE_M, alwaysContinue)
add(0xAD, LDA, ABSOLUTE, alwaysContinue)
add(0xAF, LDA, ABSOLUTE_LONG, alwaysContinue)
add(0xB1, LDA, DIRECT_INDIRECT_Y, alwaysContinue)
add(0xB2, LDA, DIRECT_INDIRECT, alwaysContinue)
add(0xB3, LDA, DIRECT_S_INDIRECT_Y, alwaysContinue)
add(0xB5, LDA, DIRECT_X, alwaysContinue)
add(0xB7, LDA, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
add(0xB9, LDA, ABSOLUTE_Y, alwaysContinue)
add(0xBD, LDA, ABSOLUTE_X, alwaysContinue)
add(0xBF, LDA, ABSOLUTE_LONG_X, alwaysContinue)
add(0xA2, LDX, IMMEDIATE_X, alwaysContinue)
add(0xA6, LDX, DIRECT, alwaysContinue)
add(0xAE, LDX, ABSOLUTE, alwaysContinue)
add(0xB6, LDX, DIRECT_Y, alwaysContinue)
add(0xBE, LDX, ABSOLUTE_Y, alwaysContinue)
add(0xA0, LDY, IMMEDIATE_X, alwaysContinue)
add(0xA4, LDY, DIRECT, alwaysContinue)
add(0xAC, LDY, ABSOLUTE, alwaysContinue)
add(0xB4, LDY, DIRECT_X, alwaysContinue)
add(0xBC, LDY, ABSOLUTE_X, alwaysContinue)
add(0x81, STA, DIRECT_X_INDIRECT, alwaysContinue)
add(0x83, STA, DIRECT_S, alwaysContinue)
add(0x85, STA, DIRECT, alwaysContinue)
add(0x87, STA, DIRECT_INDIRECT_LONG, alwaysContinue)
add(0x8D, STA, ABSOLUTE, alwaysContinue)
add(0x8F, STA, ABSOLUTE_LONG, alwaysContinue)
add(0x91, STA, DIRECT_INDIRECT_Y, alwaysContinue)
add(0x92, STA, DIRECT_INDIRECT, alwaysContinue)
add(0x93, STA, DIRECT_S_INDIRECT_Y, alwaysContinue)
add(0x95, STA, DIRECT_X, alwaysContinue)
add(0x97, STA, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
add(0x99, STA, ABSOLUTE_Y, alwaysContinue)
add(0x9D, STA, ABSOLUTE_X, alwaysContinue)
add(0x9F, STA, ABSOLUTE_LONG_X, alwaysContinue)
add(0x86, STX, DIRECT, alwaysContinue)
add(0x8E, STX, ABSOLUTE, alwaysContinue)
add(0x96, STX, DIRECT_Y, alwaysContinue)
add(0x84, STY, DIRECT, alwaysContinue)
add(0x8C, STY, ABSOLUTE, alwaysContinue)
add(0x94, STY, DIRECT_X, alwaysContinue)
add(0x64, STZ, DIRECT, alwaysContinue)
add(0x74, STZ, DIRECT_X, alwaysContinue)
add(0x9C, STZ, ABSOLUTE, alwaysContinue)
add(0x9E, STZ, ABSOLUTE_X, alwaysContinue)
add(0x48, PHA, IMPLIED, alwaysContinue) { it.preState.pushUnknown(it.preState.mWidth) }
add(0xDA, PHX, IMPLIED, alwaysContinue) { it.preState.pushUnknown(it.preState.xWidth) }
add(0x5A, PHY, IMPLIED, alwaysContinue) { it.preState.pushUnknown(it.preState.xWidth) }
add(0x68, PLA, IMPLIED, alwaysContinue) { it.preState.pull(it.preState.mWidth) }
add(0xFA, PLX, IMPLIED, alwaysContinue) { it.preState.pull(it.preState.xWidth) }
add(0x7A, PLY, IMPLIED, alwaysContinue) { it.preState.pull(it.preState.xWidth) }
add(0x8B, PHB, IMPLIED, alwaysContinue) { it.preState.pushUnknown(1) }
add(0xAB, PLB, IMPLIED, alwaysContinue) { it.preState.pull(1) }
add(0x0B, PHD, IMPLIED, alwaysContinue) { it.preState.pushUnknown(1) }
add(0x2B, PLD, IMPLIED, alwaysContinue) { it.preState.pull(1) }
add(0x4B, PHK, IMPLIED, alwaysContinue) { it.preState.push(it.address.value shr 16) }
add(0x08, PHP, IMPLIED, alwaysContinue) { it.preState.push(it.preState.flags) }
add(0x28, PLP, IMPLIED, alwaysContinue) { it.preState.pull { copy(flags = it) } }
add(0x3A, DEC, IMPLIED, alwaysContinue)
add(0xC6, DEC, DIRECT, alwaysContinue)
add(0xCE, DEC, ABSOLUTE, alwaysContinue)
add(0xD6, DEC, DIRECT_X, alwaysContinue)
add(0xDE, DEC, ABSOLUTE_X, alwaysContinue)
add(0xCA, DEX, IMPLIED, alwaysContinue)
add(0x88, DEY, IMPLIED, alwaysContinue)
add(0x1A, INC, IMPLIED, alwaysContinue)
add(0xE6, INC, DIRECT, alwaysContinue)
add(0xEE, INC, ABSOLUTE, alwaysContinue)
add(0xF6, INC, DIRECT_X, alwaysContinue)
add(0xFE, INC, ABSOLUTE_X, alwaysContinue)
add(0xE8, INX, IMPLIED, alwaysContinue)
add(0xC8, INY, IMPLIED, alwaysContinue)
add(0x06, ASL, DIRECT, alwaysContinue)
add(0x0A, ASL, IMPLIED, alwaysContinue)
add(0x0E, ASL, ABSOLUTE, alwaysContinue)
add(0x16, ASL, DIRECT_X, alwaysContinue)
add(0x1E, ASL, ABSOLUTE_X, alwaysContinue)
add(0x46, LSR, DIRECT, alwaysContinue)
add(0x4A, LSR, IMPLIED, alwaysContinue)
add(0x4E, LSR, ABSOLUTE, alwaysContinue)
add(0x56, LSR, DIRECT_X, alwaysContinue)
add(0x5E, LSR, ABSOLUTE_X, alwaysContinue)
add(0x26, ROL, DIRECT, alwaysContinue)
add(0x2A, ROL, IMPLIED, alwaysContinue)
add(0x2E, ROL, ABSOLUTE, alwaysContinue)
add(0x36, ROL, DIRECT_X, alwaysContinue)
add(0x3E, ROL, ABSOLUTE_X, alwaysContinue)
add(0x66, ROR, DIRECT, alwaysContinue)
add(0x6A, ROR, IMPLIED, alwaysContinue)
add(0x6E, ROR, ABSOLUTE, alwaysContinue)
add(0x76, ROR, DIRECT_X, alwaysContinue)
add(0x7E, ROR, ABSOLUTE_X, alwaysContinue)
add(0x61, ADC, DIRECT_X_INDIRECT, alwaysContinue)
add(0x63, ADC, DIRECT_S, alwaysContinue)
add(0x65, ADC, DIRECT, alwaysContinue)
add(0x67, ADC, DIRECT_INDIRECT_LONG, alwaysContinue)
add(0x69, ADC, IMMEDIATE_M, alwaysContinue)
add(0x6D, ADC, ABSOLUTE, alwaysContinue)
add(0x6F, ADC, ABSOLUTE_LONG, alwaysContinue)
add(0x71, ADC, DIRECT_INDIRECT_Y, alwaysContinue)
add(0x72, ADC, DIRECT_INDIRECT, alwaysContinue)
add(0x73, ADC, DIRECT_S_INDIRECT_Y, alwaysContinue)
add(0x75, ADC, DIRECT_X, alwaysContinue)
add(0x77, ADC, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
add(0x79, ADC, ABSOLUTE_Y, alwaysContinue)
add(0x7D, ADC, ABSOLUTE_X, alwaysContinue)
add(0x7F, ADC, ABSOLUTE_LONG_X, alwaysContinue)
add(0xE1, SBC, DIRECT_X_INDIRECT, alwaysContinue)