Working ROM/RAM labeling, vector table
This commit is contained in:
parent
e6445575d7
commit
5afdf84c1e
|
@ -1,9 +1,23 @@
|
||||||
{
|
{
|
||||||
"code" : {
|
"code" : {
|
||||||
|
"002140" : {
|
||||||
|
"label" : "apuIo0"
|
||||||
|
},
|
||||||
|
"002141" : {
|
||||||
|
"label" : "apuIo1"
|
||||||
|
},
|
||||||
|
"002142" : {
|
||||||
|
"label" : "apuIo2"
|
||||||
|
},
|
||||||
|
"002143" : {
|
||||||
|
"label" : "apuIo3"
|
||||||
|
},
|
||||||
|
"00420c" : {
|
||||||
|
"label" : "hdmaEnable"
|
||||||
|
},
|
||||||
"008000" : {
|
"008000" : {
|
||||||
"label" : "ResetVector"
|
"label" : "ResetVector"
|
||||||
},
|
},
|
||||||
"00800a" : { },
|
|
||||||
"00801b" : {
|
"00801b" : {
|
||||||
"comment" : "\\ Turn off emulation mode"
|
"comment" : "\\ Turn off emulation mode"
|
||||||
},
|
},
|
||||||
|
@ -58,9 +72,17 @@
|
||||||
"entries" : 28
|
"entries" : 28
|
||||||
} ]
|
} ]
|
||||||
},
|
},
|
||||||
|
"0080c9" : {
|
||||||
|
"label" : "NmiVector"
|
||||||
|
},
|
||||||
|
"00822c" : {
|
||||||
|
"label" : "UnusedVector"
|
||||||
|
},
|
||||||
|
"0082d8" : {
|
||||||
|
"label" : "IrqVector"
|
||||||
|
},
|
||||||
"00841e" : {
|
"00841e" : {
|
||||||
"label" : "ClearOam",
|
"label" : "ClearOam"
|
||||||
"comment" : "Test3"
|
|
||||||
},
|
},
|
||||||
"00879c" : {
|
"00879c" : {
|
||||||
"comment" : "Preserve Y value for later",
|
"comment" : "Preserve Y value for later",
|
||||||
|
@ -137,6 +159,9 @@
|
||||||
"00890b" : {
|
"00890b" : {
|
||||||
"comment" : "/"
|
"comment" : "/"
|
||||||
},
|
},
|
||||||
|
"00ffff" : {
|
||||||
|
"label" : "CrashVector"
|
||||||
|
},
|
||||||
"0287d0" : {
|
"0287d0" : {
|
||||||
"flags" : [ {
|
"flags" : [ {
|
||||||
"flagType" : "JslTableRoutine",
|
"flagType" : "JslTableRoutine",
|
||||||
|
@ -151,6 +176,15 @@
|
||||||
"flagType" : "JslTableRoutine",
|
"flagType" : "JslTableRoutine",
|
||||||
"entries" : 12
|
"entries" : 12
|
||||||
} ]
|
} ]
|
||||||
|
},
|
||||||
|
"7e0010" : {
|
||||||
|
"label" : "gameMode"
|
||||||
|
},
|
||||||
|
"7e0011" : {
|
||||||
|
"label" : "subGameMode"
|
||||||
|
},
|
||||||
|
"7e0012" : {
|
||||||
|
"label" : "nmiExecuted"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -60,9 +60,6 @@ class Grid {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun add(ins: CodeUnit, metadata: Metadata, disassembly: Disassembly) {
|
fun add(ins: CodeUnit, metadata: Metadata, disassembly: Disassembly) {
|
||||||
val insMetadata = ins.address?.let { metadata[it] }
|
|
||||||
|
|
||||||
val actualAddress = ins.address
|
|
||||||
val presentedAddress = ins.presentedAddress
|
val presentedAddress = ins.presentedAddress
|
||||||
|
|
||||||
if (nextAddress != null) {
|
if (nextAddress != null) {
|
||||||
|
@ -75,18 +72,31 @@ class Grid {
|
||||||
val y = (height++)
|
val y = (height++)
|
||||||
addresses[presentedAddress] = y
|
addresses[presentedAddress] = y
|
||||||
|
|
||||||
add(y, ins.address,
|
val (address, bytes, label, primaryMnemonic, secondaryMnemonic, suffix, operands, state, comment, labelAddress)
|
||||||
text(actualAddress?.toFormattedString() ?: ""),
|
= ins.print(metadata)
|
||||||
text(ins.bytesToString()),
|
|
||||||
editableField(presentedAddress, "label", insMetadata?.label),
|
|
||||||
fragment {
|
|
||||||
opcodeNode(ins)
|
|
||||||
text(" ")
|
|
||||||
var operands = ins.printOperands()
|
|
||||||
|
|
||||||
|
add(y, ins.address,
|
||||||
|
text(address ?: ""),
|
||||||
|
text(bytes),
|
||||||
|
editableField(presentedAddress, "label", label),
|
||||||
|
fragment {
|
||||||
|
if (secondaryMnemonic == null) {
|
||||||
|
text(primaryMnemonic)
|
||||||
|
} else {
|
||||||
|
span {
|
||||||
|
text(primaryMnemonic)
|
||||||
|
}.attr("title", secondaryMnemonic).addClass("opcode-info")
|
||||||
|
}
|
||||||
|
text(suffix ?: "")
|
||||||
|
text(" ")
|
||||||
val link = ins.linkedState
|
val link = ins.linkedState
|
||||||
if (link == null) {
|
if (link == null) {
|
||||||
text(operands)
|
if (labelAddress == null) {
|
||||||
|
text(operands ?: "")
|
||||||
|
} else {
|
||||||
|
val currentLabel = metadata[labelAddress]?.label
|
||||||
|
editablePopupField(labelAddress, "label", operands, currentLabel)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val local = link.address in disassembly
|
val local = link.address in disassembly
|
||||||
|
|
||||||
|
@ -95,15 +105,15 @@ class Grid {
|
||||||
else -> "/${link.address.toSimpleString()}/${link.urlString}"
|
else -> "/${link.address.toSimpleString()}/${link.urlString}"
|
||||||
}
|
}
|
||||||
|
|
||||||
operands = metadata[link.address]?.label ?: operands
|
//operands = metadata[link.address]?.label ?: operands
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text(operands)
|
text(operands ?: "")
|
||||||
}.attr("href", url)
|
}.attr("href", url)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
text(ins.postState?.toString() ?: ""),
|
text(state ?: ""),
|
||||||
editableField(presentedAddress, "comment", insMetadata?.comment)
|
editableField(presentedAddress, "comment", comment)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ins.opcode.continuation == Continuation.NO) {
|
if (ins.opcode.continuation == Continuation.NO) {
|
||||||
|
@ -111,18 +121,6 @@ class Grid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun HtmlArea.opcodeNode(ins: CodeUnit) {
|
|
||||||
val alt = ins.printAlternativeOpcodeAndSuffix()
|
|
||||||
if (alt == null) {
|
|
||||||
text(ins.printOpcodeAndSuffix())
|
|
||||||
} else {
|
|
||||||
span {
|
|
||||||
text(ins.printOpcodeAndSuffix())
|
|
||||||
}.attr("title", alt).addClass("opcode-info")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun editableField(address: SnesAddress, type: String, value: String?): HtmlNode {
|
private fun editableField(address: SnesAddress, type: String, value: String?): HtmlNode {
|
||||||
return input(value = value ?: "")
|
return input(value = value ?: "")
|
||||||
.addClass("field-$type")
|
.addClass("field-$type")
|
||||||
|
@ -131,6 +129,18 @@ class Grid {
|
||||||
.attr("data-address", address.toSimpleString())
|
.attr("data-address", address.toSimpleString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun HtmlArea.editablePopupField(address: SnesAddress, type: String, displayValue: String?, editValue: String?) {
|
||||||
|
span {
|
||||||
|
text(displayValue ?: "")
|
||||||
|
span {}.addClass("field-editable-popup-icon")
|
||||||
|
}
|
||||||
|
.addClass("field-$type")
|
||||||
|
.addClass("field-editable-popup")
|
||||||
|
.attr("data-field", type)
|
||||||
|
.attr("data-value", editValue ?: "")
|
||||||
|
.attr("data-address", address.toSimpleString())
|
||||||
|
}
|
||||||
|
|
||||||
private fun addDummy() {
|
private fun addDummy() {
|
||||||
val y = (height++)
|
val y = (height++)
|
||||||
add(y, null, null, null, null, text("..."), null, null)
|
add(y, null, null, null, null, text("..."), null, null)
|
||||||
|
|
|
@ -9,6 +9,21 @@ import kotlin.reflect.KMutableProperty1
|
||||||
|
|
||||||
private val RESET_VECTOR_LOCATION = address(0x00_FFFC)
|
private val RESET_VECTOR_LOCATION = address(0x00_FFFC)
|
||||||
|
|
||||||
|
private val VECTORS = listOf(
|
||||||
|
address(0x00_FFE4) to "COP",
|
||||||
|
address(0x00_FFE6) to "BRK",
|
||||||
|
address(0x00_FFE8) to "ABORT",
|
||||||
|
address(0x00_FFEA) to "NMI",
|
||||||
|
address(0x00_FFEC) to "RESET",
|
||||||
|
address(0x00_FFEE) to "IRQ",
|
||||||
|
address(0x00_FFF4) to "COP (e)",
|
||||||
|
address(0x00_FFF6) to "BRK (e)",
|
||||||
|
address(0x00_FFF8) to "ABORT (e)",
|
||||||
|
address(0x00_FFFA) to "NMI (e)",
|
||||||
|
address(0x00_FFFC) to "RES (e)",
|
||||||
|
address(0x00_FFFE) to "IRQBRK (e)"
|
||||||
|
)
|
||||||
|
|
||||||
object Service {
|
object Service {
|
||||||
private const val romName = "Zelda no Densetsu - Kamigami no Triforce (Japan)"
|
private const val romName = "Zelda no Densetsu - Kamigami no Triforce (Japan)"
|
||||||
private val romDir = Paths.get("""P:\Emulation\ROMs\SNES""")
|
private val romDir = Paths.get("""P:\Emulation\ROMs\SNES""")
|
||||||
|
@ -30,7 +45,7 @@ object Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showDisassembly(initialAddress: SnesAddress, flags: VagueNumber): HtmlNode? {
|
fun showDisassembly(initialAddress: SnesAddress, flags: VagueNumber): HtmlNode? {
|
||||||
val initialState = State(memory = snesMemory, address = initialAddress, flags = flags)
|
val initialState = State(memory = snesMemory, address = initialAddress, flags = flags, metadata = metadata)
|
||||||
val disassembly = Disassembler.disassemble(initialState, metadata, false)
|
val disassembly = Disassembler.disassemble(initialState, metadata, false)
|
||||||
|
|
||||||
return print(disassembly, metadata)
|
return print(disassembly, metadata)
|
||||||
|
@ -52,17 +67,7 @@ object Service {
|
||||||
.sortedBy { it.first distanceTo it.second }
|
.sortedBy { it.first distanceTo it.second }
|
||||||
.forEach { grid.arrow(it.first, it.second) }
|
.forEach { grid.arrow(it.first, it.second) }
|
||||||
|
|
||||||
return html {
|
return grid.output()
|
||||||
head {
|
|
||||||
title { text("Disassembly Browser") }
|
|
||||||
link {}.attr("rel", "stylesheet").attr("href", "/resources/style.css")
|
|
||||||
meta {}.attr("charset", "UTF-8")
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
grid.output().appendTo(parent)
|
|
||||||
script().attr("src", "/resources/disbrowser.js")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateMetadata(address: SnesAddress, field: KMutableProperty1<MetadataLine, String?>, value: String) {
|
fun updateMetadata(address: SnesAddress, field: KMutableProperty1<MetadataLine, String?>, value: String) {
|
||||||
|
@ -79,11 +84,17 @@ object Service {
|
||||||
val line = metadata.getOrCreate(address)
|
val line = metadata.getOrCreate(address)
|
||||||
field.set(line, value)
|
field.set(line, value)
|
||||||
|
|
||||||
if (line.isEmpty()) {
|
metadata.cleanUp()
|
||||||
metadata[address] = null
|
|
||||||
}
|
|
||||||
|
|
||||||
metaFile.save(metadata)
|
metaFile.save(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getVectors() = VECTORS.asSequence()
|
||||||
|
.map { (vectorLocation: SnesAddress, name: String ) ->
|
||||||
|
val codeLocation = SnesAddress(snesMemory.getWord(vectorLocation)!!.toUInt24())
|
||||||
|
val label = metadata[codeLocation]?.label
|
||||||
|
?: codeLocation.toFormattedString()
|
||||||
|
Vector(vectorLocation, codeLocation, name, label)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Vector(val vectorLocation: SnesAddress, val codeLocation: SnesAddress, val name: String, val label: String)
|
|
@ -16,18 +16,9 @@ interface CodeUnit {
|
||||||
val opcode: Opcode
|
val opcode: Opcode
|
||||||
val lengthSuffix: String?
|
val lengthSuffix: String?
|
||||||
|
|
||||||
|
val memory: SnesMapper
|
||||||
|
|
||||||
fun operandByte(index: UInt): UByte = bytes[opcode.operandIndex + index]
|
fun operandByte(index: UInt): UByte = bytes[opcode.operandIndex + index]
|
||||||
fun printOpcodeAndSuffix(): String {
|
|
||||||
val mnemonic = opcode.mnemonic.displayName
|
|
||||||
val suffix = lengthSuffix ?: ""
|
|
||||||
return "$mnemonic$suffix"
|
|
||||||
}
|
|
||||||
fun printAlternativeOpcodeAndSuffix(): String? {
|
|
||||||
val mnemonic = opcode.mnemonic.alternativeName ?: return null
|
|
||||||
val suffix = lengthSuffix ?: ""
|
|
||||||
return "$mnemonic$suffix"
|
|
||||||
}
|
|
||||||
fun printOperands() = opcode.mode.print(this)
|
|
||||||
|
|
||||||
fun bytesToString(): String {
|
fun bytesToString(): String {
|
||||||
return bytes.asSequence()
|
return bytes.asSequence()
|
||||||
|
@ -65,7 +56,8 @@ class DataBlock(
|
||||||
override val bytes: ValidMemorySpace,
|
override val bytes: ValidMemorySpace,
|
||||||
override val presentedAddress: SnesAddress,
|
override val presentedAddress: SnesAddress,
|
||||||
override val relativeAddress: SnesAddress,
|
override val relativeAddress: SnesAddress,
|
||||||
override val linkedState: State?
|
override val linkedState: State?,
|
||||||
|
override val memory: SnesMapper
|
||||||
) : CodeUnit {
|
) : CodeUnit {
|
||||||
override val nextPresentedAddress: SnesAddress
|
override val nextPresentedAddress: SnesAddress
|
||||||
get() = presentedAddress + operandLength.toInt()
|
get() = presentedAddress + operandLength.toInt()
|
||||||
|
@ -78,6 +70,7 @@ class DataBlock(
|
||||||
}
|
}
|
||||||
|
|
||||||
class Instruction(override val bytes: ValidMemorySpace, override val opcode: Opcode, override val preState: State) : CodeUnit {
|
class Instruction(override val bytes: ValidMemorySpace, override val opcode: Opcode, override val preState: State) : CodeUnit {
|
||||||
|
override val memory = preState.memory
|
||||||
override val address: SnesAddress get() = preState.address
|
override val address: SnesAddress get() = preState.address
|
||||||
override val relativeAddress get() = address
|
override val relativeAddress get() = address
|
||||||
override val presentedAddress get() = address
|
override val presentedAddress get() = address
|
||||||
|
@ -92,9 +85,11 @@ class Instruction(override val bytes: ValidMemorySpace, override val opcode: Opc
|
||||||
.withOrigin(this)
|
.withOrigin(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val showLengthSuffix get() = opcode.mode.showLengthSuffix and opcode.mnemonic.showLengthSuffix
|
||||||
|
|
||||||
override val lengthSuffix: String?
|
override val lengthSuffix: String?
|
||||||
get() {
|
get() {
|
||||||
if (!opcode.mode.showLengthSuffix) {
|
if (!showLengthSuffix) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,58 +111,55 @@ class Instruction(override val bytes: ValidMemorySpace, override val opcode: Opc
|
||||||
}
|
}
|
||||||
|
|
||||||
return referencedAddress()
|
return referencedAddress()
|
||||||
|
|
||||||
// return when (opcode.mode) {
|
|
||||||
// Mode.ABSOLUTE_CODE -> preState.resolveAbsoluteCode(word)
|
|
||||||
// Mode.ABSOLUTE_LONG -> SnesAddress(long)
|
|
||||||
// Mode.RELATIVE -> relativeAddress + 2 + signedByte.toInt()
|
|
||||||
// Mode.RELATIVE_LONG -> relativeAddress + 3 + signedWord.toInt()
|
|
||||||
// Mode.CODE_WORD -> preState.resolveAbsoluteCode(word)
|
|
||||||
// Mode.CODE_LONG -> SnesAddress(long)
|
|
||||||
// Mode.DATA_WORD -> preState.resolveAbsoluteData(word)
|
|
||||||
// Mode.DATA_LONG -> SnesAddress(long)
|
|
||||||
// else -> null
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun referencedAddress(): SnesAddress? {
|
private fun referencedAddress() = opcode.mode.referencedAddress(this)
|
||||||
return when (opcode.mode) {
|
|
||||||
Mode.ABSOLUTE -> preState.resolveAbsoluteData(word)
|
|
||||||
Mode.ABSOLUTE_CODE -> preState.resolveAbsoluteCode(word)
|
|
||||||
Mode.ABSOLUTE_INDIRECT -> preState.resolveAbsoluteData(word)
|
|
||||||
Mode.ABSOLUTE_INDIRECT_LONG -> preState.resolveAbsoluteData(word)
|
|
||||||
Mode.ABSOLUTE_LONG -> SnesAddress(long)
|
|
||||||
Mode.ABSOLUTE_LONG_X -> SnesAddress(long)
|
|
||||||
Mode.ABSOLUTE_X -> preState.resolveAbsoluteData(word)
|
|
||||||
Mode.ABSOLUTE_X_INDIRECT -> preState.resolveAbsoluteData(word)
|
|
||||||
Mode.ABSOLUTE_Y -> preState.resolveAbsoluteData(word)
|
|
||||||
Mode.BLOCK_MOVE -> null
|
|
||||||
Mode.CODE_WORD -> preState.resolveAbsoluteCode(word)
|
|
||||||
Mode.CODE_LONG -> SnesAddress(long)
|
|
||||||
Mode.DATA_BYTE -> null
|
|
||||||
Mode.DATA_WORD -> preState.resolveAbsoluteData(word)
|
|
||||||
Mode.DATA_LONG -> SnesAddress(long)
|
|
||||||
Mode.DIRECT -> preState.resolveDirectPage(byte)
|
|
||||||
Mode.DIRECT_X -> preState.resolveDirectPage(byte)
|
|
||||||
Mode.DIRECT_Y -> preState.resolveDirectPage(byte)
|
|
||||||
Mode.DIRECT_S -> null
|
|
||||||
Mode.DIRECT_INDIRECT -> preState.resolveDirectPage(byte)
|
|
||||||
Mode.DIRECT_INDIRECT_Y -> preState.resolveDirectPage(byte)
|
|
||||||
Mode.DIRECT_X_INDIRECT -> preState.resolveDirectPage(byte)
|
|
||||||
Mode.DIRECT_S_INDIRECT_Y -> null
|
|
||||||
Mode.DIRECT_INDIRECT_LONG -> preState.resolveDirectPage(byte)
|
|
||||||
Mode.DIRECT_INDIRECT_LONG_Y -> preState.resolveDirectPage(byte)
|
|
||||||
Mode.IMMEDIATE_8 -> null
|
|
||||||
Mode.IMMEDIATE_16 -> null
|
|
||||||
Mode.IMMEDIATE_M -> null
|
|
||||||
Mode.IMMEDIATE_X -> null
|
|
||||||
Mode.IMPLIED -> null
|
|
||||||
Mode.RELATIVE -> relativeAddress + 2 + signedByte.toInt()
|
|
||||||
Mode.RELATIVE_LONG -> relativeAddress + 3 + signedWord.toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$address ${bytesToString()} ${opcode.mnemonic.displayName} ${opcode.mode.print(this).padEnd(100, ' ')} ($preState -> $postState)"
|
val (address, bytes, _, primaryMnemonic, _, suffix, operands, _, _) = print()
|
||||||
|
return "${address ?: "\$xx:xxxx"} $bytes $primaryMnemonic${suffix ?: ""} ${operands?.padEnd(100, ' ')
|
||||||
|
?: ""} ($preState -> $postState)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun CodeUnit.print(metadata: Metadata? = null): PrintedCodeUnit {
|
||||||
|
val mnemonic = opcode.mnemonic
|
||||||
|
val primaryMnemonic = mnemonic.displayName
|
||||||
|
val secondaryMnemonic = mnemonic.alternativeName
|
||||||
|
|
||||||
|
var suffix = lengthSuffix
|
||||||
|
var operands = metadata?.let { opcode.mode.printWithLabel(this, it) }
|
||||||
|
if (operands == null) {
|
||||||
|
operands = opcode.mode.printRaw(this)
|
||||||
|
suffix = null
|
||||||
|
}
|
||||||
|
|
||||||
|
val state = postState?.toString()
|
||||||
|
val label = address?.let { metadata?.get(it)?.label }
|
||||||
|
val comment = address?.let { metadata?.get(it)?.comment }
|
||||||
|
val formattedAddress = address?.toFormattedString()
|
||||||
|
val bytes = bytesToString()
|
||||||
|
|
||||||
|
val labelAddress = if (opcode.mode.canHaveLabel) {
|
||||||
|
opcode.mode.referencedAddress(this)?.let {
|
||||||
|
memory.toCanonical(it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
return PrintedCodeUnit(formattedAddress, bytes, label, primaryMnemonic, secondaryMnemonic, suffix, operands, state, comment, labelAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class PrintedCodeUnit(
|
||||||
|
val address: String?,
|
||||||
|
val bytes: String,
|
||||||
|
val label: String?,
|
||||||
|
val primaryMnemonic: String,
|
||||||
|
val secondaryMnemonic: String?,
|
||||||
|
val suffix: String?,
|
||||||
|
val operands: String?,
|
||||||
|
val state: String?,
|
||||||
|
val comment: String?,
|
||||||
|
val labelAddress: SnesAddress?
|
||||||
|
)
|
|
@ -6,12 +6,13 @@ import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||||
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
|
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
||||||
import com.smallhacker.disbrowser.util.joinNullableBytes
|
import com.smallhacker.disbrowser.util.joinNullableBytes
|
||||||
|
import com.smallhacker.disbrowser.util.removeIf
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.disbrowser.util.toUInt24
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class Metadata {
|
class Metadata {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private val code: TreeMap<SnesAddress, MetadataLine>
|
private val code: MutableMap<SnesAddress, MetadataLine>
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.code = TreeMap()
|
this.code = TreeMap()
|
||||||
|
@ -46,6 +47,10 @@ class Metadata {
|
||||||
this[address] = newLine
|
this[address] = newLine
|
||||||
return newLine
|
return newLine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cleanUp() {
|
||||||
|
code.removeIf { _, v -> v.isEmpty() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "flagType")
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "flagType")
|
||||||
|
@ -88,13 +93,14 @@ class JmpIndirectLongInterleavedTable @JsonCreator constructor(
|
||||||
val target = table.getLong(offset)
|
val target = table.getLong(offset)
|
||||||
|
|
||||||
DataBlock(
|
DataBlock(
|
||||||
Opcode.POINTER_LONG,
|
Opcode.CODE_POINTER_LONG,
|
||||||
table.range(offset, 3u),
|
table.range(offset, 3u),
|
||||||
jumpInstruction.postState.address + offset.toInt(),
|
jumpInstruction.postState.address + offset.toInt(),
|
||||||
jumpInstruction.relativeAddress,
|
jumpInstruction.relativeAddress,
|
||||||
jumpInstruction.opcode.mutate(jumpInstruction)
|
jumpInstruction.opcode.mutate(jumpInstruction)
|
||||||
.mutateAddress { SnesAddress(target) }
|
.mutateAddress { SnesAddress(target) }
|
||||||
.withOrigin(jumpInstruction)
|
.withOrigin(jumpInstruction),
|
||||||
|
jumpInstruction.memory
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,5 +12,5 @@ data class MetadataLine(
|
||||||
val flags: MutableList<InstructionFlag> = ArrayList()
|
val flags: MutableList<InstructionFlag> = ArrayList()
|
||||||
) {
|
) {
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun isEmpty() = (label == null) && (comment == null) && (preComment == null) && (length == 0) && (flags.isEmpty())
|
fun isEmpty() = (label == null) && (comment == null) && (preComment == null) && (length == null) && (flags.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
enum class Mnemonic(private val nameOverride: String? = null, val alternativeName: String? = null) {
|
enum class Mnemonic(private val nameOverride: String? = null, val alternativeName: String? = null, val showLengthSuffix: Boolean = true) {
|
||||||
ADC, AND, ASL, BCC(alternativeName = "BLT"), BCS(alternativeName = "BGE"), BEQ, BIT, BMI, BNE, BPL, BRA,
|
ADC, AND, ASL, BCC(alternativeName = "BLT"), BCS(alternativeName = "BGE"), BEQ, BIT, BMI, BNE, BPL, BRA,
|
||||||
BRK, BRL, BVC, BVS, CLC, CLD, CLI, CLV, CMP, COP, CPX,
|
BRK, BRL, BVC, BVS, CLC, CLD, CLI, CLV, CMP, COP, CPX,
|
||||||
CPY, DEC, DEX, DEY, EOR, INC, INX, INY, JMP, JML, JSL,
|
CPY, DEC, DEX, DEY, EOR, INC, INX, INY, JMP(showLengthSuffix = false), JML(showLengthSuffix = false), JSL(showLengthSuffix = false),
|
||||||
JSR, LDA, LDX, LDY, LSR, MVN, MVP, NOP, ORA, PEA, PEI,
|
JSR(showLengthSuffix = false), LDA, LDX, LDY, LSR, MVN, MVP, NOP, ORA, PEA, PEI,
|
||||||
PER, PHA, PHB, PHD, PHK, PHP, PHX, PHY, PLA, PLB, PLD,
|
PER, PHA, PHB, PHD, PHK, PHP, PHX, PHY, PLA, PLB, PLD,
|
||||||
PLP, PLX, PLY, REP, ROL, ROR, RTI, RTL, RTS, SBC, SEC,
|
PLP, PLX, PLY, REP, ROL, ROR, RTI, RTL, RTS, SBC, SEC,
|
||||||
SED, SEI, SEP, STA, STP, STX, STY, STZ, TAX, TAY, TCD,
|
SED, SEI, SEP, STA, STP, STX, STY, STZ, TAX, TAY, TCD,
|
||||||
|
|
|
@ -2,144 +2,228 @@ package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.util.*
|
import com.smallhacker.disbrowser.util.*
|
||||||
|
|
||||||
private val ZEROES = Regex("[0]+")
|
interface Mode {
|
||||||
private fun countBytes(format: String) = (ZEROES.find(format)?.groupValues?.firstOrNull()?.length?.toUInt() ?: 0u) / 2u
|
val dataMode get() = false
|
||||||
|
val showLengthSuffix get() = true
|
||||||
private const val ACCUMULATOR_SIZE = -1
|
val canHaveLabel: Boolean
|
||||||
private const val INDEX_SIZE = -2
|
fun operandLength(state: State): UInt?
|
||||||
|
fun printWithLabel(ins: CodeUnit, metadata: Metadata): String? = referencedAddress(ins)?.let { metadata[it]?.label }
|
||||||
fun format(format: String, value: UInt, operandBytes: UInt = countBytes(format)) =
|
fun printRaw(ins: CodeUnit): String
|
||||||
format.replace(ZEROES, toHex(value, operandBytes))
|
fun referencedAddress(ins: CodeUnit): SnesAddress?
|
||||||
|
|
||||||
enum class Mode {
|
|
||||||
DATA_BYTE("$00", dataMode = true, showLengthSuffix = false),
|
|
||||||
DATA_WORD("$0000", dataMode = true, showLengthSuffix = false),
|
|
||||||
DATA_LONG("$000000", dataMode = true, showLengthSuffix = false),
|
|
||||||
CODE_WORD("$0000", dataMode = true, showLengthSuffix = false),
|
|
||||||
CODE_LONG("$000000", dataMode = true, showLengthSuffix = false),
|
|
||||||
|
|
||||||
IMPLIED("", showLengthSuffix = false),
|
|
||||||
IMMEDIATE_8("#$00", showLengthSuffix = false),
|
|
||||||
IMMEDIATE_16("#$0000", showLengthSuffix = false),
|
|
||||||
ABSOLUTE("$0000"),
|
|
||||||
ABSOLUTE_CODE("$0000"),
|
|
||||||
ABSOLUTE_X("$0000,x"),
|
|
||||||
ABSOLUTE_Y("$0000,y"),
|
|
||||||
ABSOLUTE_LONG("$000000"),
|
|
||||||
ABSOLUTE_LONG_X("$000000,x"),
|
|
||||||
ABSOLUTE_INDIRECT("($0000)"),
|
|
||||||
ABSOLUTE_INDIRECT_LONG("[$0000]"),
|
|
||||||
ABSOLUTE_X_INDIRECT("($0000,x)"),
|
|
||||||
DIRECT("$00"),
|
|
||||||
DIRECT_X("$00,x"),
|
|
||||||
DIRECT_Y("$00,y"),
|
|
||||||
DIRECT_S("$00,s"),
|
|
||||||
DIRECT_INDIRECT("($00)"),
|
|
||||||
DIRECT_INDIRECT_Y("($00),y"),
|
|
||||||
DIRECT_X_INDIRECT("($00,x)"),
|
|
||||||
DIRECT_S_INDIRECT_Y("($00,s),y"),
|
|
||||||
DIRECT_INDIRECT_LONG("[$00]"),
|
|
||||||
DIRECT_INDIRECT_LONG_Y("[$00],y"),
|
|
||||||
|
|
||||||
IMMEDIATE_M(
|
|
||||||
operandLength = ACCUMULATOR_SIZE,
|
|
||||||
print = {
|
|
||||||
when (preState?.m) {
|
|
||||||
null -> "????"
|
|
||||||
true -> format("#$00", byte.toUInt())
|
|
||||||
false -> format("#$0000", word.toUInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
IMMEDIATE_X(
|
|
||||||
operandLength = INDEX_SIZE,
|
|
||||||
print = {
|
|
||||||
when (preState?.x) {
|
|
||||||
null -> "???"
|
|
||||||
true -> format("#$00", byte.toUInt())
|
|
||||||
false -> format("#$0000", word.toUInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
|
|
||||||
RELATIVE(
|
|
||||||
format = "$000000",
|
|
||||||
operandLength = 1u,
|
|
||||||
valueGetter = {
|
|
||||||
val rel = signedByte.toInt() + 2
|
|
||||||
(relativeAddress + rel).value.toUInt()
|
|
||||||
},
|
|
||||||
showLengthSuffix = false
|
|
||||||
),
|
|
||||||
RELATIVE_LONG(
|
|
||||||
format = "$000000",
|
|
||||||
operandLength = 2u,
|
|
||||||
valueGetter = {
|
|
||||||
val rel = signedWord.toInt() + 3
|
|
||||||
(relativeAddress + rel).value.toUInt()
|
|
||||||
},
|
|
||||||
showLengthSuffix = false
|
|
||||||
),
|
|
||||||
BLOCK_MOVE(
|
|
||||||
operandLength = 2,
|
|
||||||
print = { String.format("#$%02x,#$%02x", byte.toInt(), byte2.toInt()) },
|
|
||||||
showLengthSuffix = false
|
|
||||||
)
|
|
||||||
;
|
|
||||||
|
|
||||||
private val operandLength: Int
|
|
||||||
val print: CodeUnit.() -> String
|
|
||||||
val dataMode: Boolean
|
|
||||||
val showLengthSuffix: Boolean
|
|
||||||
|
|
||||||
constructor(operandLength: Int, print: CodeUnit.() -> String, showLengthSuffix: Boolean = true) {
|
|
||||||
this.operandLength = operandLength
|
|
||||||
this.print = print
|
|
||||||
this.dataMode = false
|
|
||||||
this.showLengthSuffix = showLengthSuffix
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
format: String,
|
|
||||||
printedLength: UInt = countBytes(format),
|
|
||||||
operandLength: UInt = printedLength,
|
|
||||||
valueGetter: CodeUnit.() -> UInt = { value!! },
|
|
||||||
dataMode: Boolean = false,
|
|
||||||
showLengthSuffix: Boolean = true
|
|
||||||
) {
|
|
||||||
this.operandLength = operandLength.toInt()
|
|
||||||
this.print = { format(format, valueGetter(this), printedLength) }
|
|
||||||
this.dataMode = dataMode
|
|
||||||
this.showLengthSuffix = showLengthSuffix
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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): UInt? {
|
|
||||||
val operatorLength = if (this.dataMode) 0u else 1u
|
|
||||||
return operandLength(state)
|
|
||||||
?.plus(operatorLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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): UInt? {
|
|
||||||
return when (operandLength) {
|
|
||||||
ACCUMULATOR_SIZE -> state.mWidth
|
|
||||||
INDEX_SIZE -> state.xWidth
|
|
||||||
else -> operandLength.toUInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Mode.instructionLength(state: State): UInt? {
|
||||||
|
val operatorLength = if (this.dataMode) 0u else 1u
|
||||||
|
return operandLength(state)
|
||||||
|
?.plus(operatorLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
val Mode.x: Mode get() = IndexXMode(this)
|
||||||
|
val Mode.y: Mode get() = IndexYMode(this)
|
||||||
|
val Mode.s: Mode get() = IndexSMode(this)
|
||||||
|
val Mode.indirect: Mode get() = IndirectMode(this)
|
||||||
|
val Mode.indirectLong: Mode get() = IndirectLongMode(this)
|
||||||
|
|
||||||
|
private abstract class RawWrappedMode(
|
||||||
|
private val parent: Mode,
|
||||||
|
private val prefix: String = "",
|
||||||
|
private val suffix: String = ""
|
||||||
|
) : Mode by parent {
|
||||||
|
override val canHaveLabel = false
|
||||||
|
override fun printWithLabel(ins: CodeUnit, metadata: Metadata): String? = printRaw(ins)
|
||||||
|
override fun printRaw(ins: CodeUnit) = prefix + parent.printRaw(ins) + suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class WrappedMode(
|
||||||
|
private val parent: Mode,
|
||||||
|
private val prefix: String = "",
|
||||||
|
private val suffix: String = ""
|
||||||
|
) : Mode by parent {
|
||||||
|
override fun printWithLabel(ins: CodeUnit, metadata: Metadata): String? = parent.printWithLabel(ins, metadata)?.let { prefix + it + suffix }
|
||||||
|
override fun printRaw(ins: CodeUnit) = prefix + parent.printRaw(ins) + suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MultiMode(private val fallback: Mode, private vararg val options: Mode) : Mode {
|
||||||
|
override val canHaveLabel = get{ canHaveLabel }
|
||||||
|
override val dataMode = get { dataMode }
|
||||||
|
override val showLengthSuffix = get { showLengthSuffix }
|
||||||
|
|
||||||
|
override fun operandLength(state: State) = get(state) { operandLength(state) }
|
||||||
|
|
||||||
|
override fun printWithLabel(ins: CodeUnit, metadata: Metadata): String? = get(ins.preState) { printWithLabel(ins, metadata) }
|
||||||
|
|
||||||
|
override fun printRaw(ins: CodeUnit) = get(ins.preState) { printRaw(ins) }
|
||||||
|
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = get(ins.preState) { referencedAddress(ins) }
|
||||||
|
|
||||||
|
protected abstract fun pickMode(state: State): UInt
|
||||||
|
|
||||||
|
private fun <T> get(state: State?, getter: Mode.() -> T): T {
|
||||||
|
if (state != null) {
|
||||||
|
val mode = pickMode(state)
|
||||||
|
if (mode != 0u) {
|
||||||
|
return getter(options[mode.toInt() - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getter(fallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : Any> get(getter: Mode.() -> T) = get(null, getter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface DataValueType {
|
||||||
|
fun resolve(byte: UByte, state: State?, memory: SnesMapper): SnesAddress?
|
||||||
|
fun resolve(word: UShort, state: State?, memory: SnesMapper): SnesAddress?
|
||||||
|
fun resolve(long: UInt24, state: State?, memory: SnesMapper): SnesAddress?
|
||||||
|
val canHaveLabel: Boolean
|
||||||
|
|
||||||
|
val byte: Mode get() = DataByteMode(this)
|
||||||
|
val word: Mode get() = DataWordMode(this)
|
||||||
|
val long: Mode get() = DataLongMode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object DirectData : DataValueType {
|
||||||
|
override val canHaveLabel = false
|
||||||
|
override fun resolve(byte: UByte, state: State?, memory: SnesMapper): SnesAddress? = null
|
||||||
|
override fun resolve(word: UShort, state: State?, memory: SnesMapper): SnesAddress? = null
|
||||||
|
override fun resolve(long: UInt24, state: State?, memory: SnesMapper): SnesAddress? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
object DataPointer : DataValueType {
|
||||||
|
override val canHaveLabel = true
|
||||||
|
override fun resolve(byte: UByte, state: State?, memory: SnesMapper) = state?.resolveDirectPage(byte)
|
||||||
|
override fun resolve(word: UShort, state: State?, memory: SnesMapper) = state?.resolveAbsoluteData(word)
|
||||||
|
override fun resolve(long: UInt24, state: State?, memory: SnesMapper) = memory.toCanonical(SnesAddress(long))
|
||||||
|
}
|
||||||
|
|
||||||
|
object CodePointer : DataValueType {
|
||||||
|
override val canHaveLabel = true
|
||||||
|
override fun resolve(byte: UByte, state: State?, memory: SnesMapper) = state?.resolveDirectPage(byte)
|
||||||
|
override fun resolve(word: UShort, state: State?, memory: SnesMapper) = state?.resolveAbsoluteCode(word)
|
||||||
|
override fun resolve(long: UInt24, state: State?, memory: SnesMapper) = memory.toCanonical(SnesAddress(long))
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class DataValueMode(private val length: UInt, protected val type: DataValueType) : Mode {
|
||||||
|
override val canHaveLabel = type.canHaveLabel
|
||||||
|
override val dataMode = true
|
||||||
|
override val showLengthSuffix = false
|
||||||
|
override fun operandLength(state: State) = length
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DataByteMode(type: DataValueType) : DataValueMode(1u, type) {
|
||||||
|
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.byte)
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.byte, ins.preState, ins.memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DataWordMode(type: DataValueType) : DataValueMode(2u, type) {
|
||||||
|
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.word)
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.word, ins.preState, ins.memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DataLongMode(type: DataValueType) : DataValueMode(3u, type) {
|
||||||
|
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.long)
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.long, ins.preState, ins.memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Implied : Mode {
|
||||||
|
override val canHaveLabel = false
|
||||||
|
override val showLengthSuffix = false
|
||||||
|
override fun operandLength(state: State) = 0u
|
||||||
|
override fun printRaw(ins: CodeUnit) = ""
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
object Immediate8 : Mode {
|
||||||
|
override val canHaveLabel = false
|
||||||
|
override val showLengthSuffix = false
|
||||||
|
override fun operandLength(state: State) = 1u
|
||||||
|
override fun printRaw(ins: CodeUnit) = "#$" + toHex(ins.byte)
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
object Immediate16 : Mode {
|
||||||
|
override val canHaveLabel = false
|
||||||
|
override val showLengthSuffix = false
|
||||||
|
override fun operandLength(state: State) = 2u
|
||||||
|
override fun printRaw(ins: CodeUnit) = "#$" + toHex(ins.word)
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
object Direct : Mode {
|
||||||
|
override val canHaveLabel = true
|
||||||
|
override fun operandLength(state: State) = 1u
|
||||||
|
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.byte)
|
||||||
|
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveDirectPage(ins.byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Absolute : Mode {
|
||||||
|
override val canHaveLabel = true
|
||||||
|
override fun operandLength(state: State) = 2u
|
||||||
|
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.word)
|
||||||
|
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveAbsoluteData(ins.word)
|
||||||
|
}
|
||||||
|
|
||||||
|
object AbsoluteCode : Mode {
|
||||||
|
override val canHaveLabel = true
|
||||||
|
override fun operandLength(state: State) = 2u
|
||||||
|
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.word)
|
||||||
|
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveAbsoluteCode(ins.word)
|
||||||
|
}
|
||||||
|
|
||||||
|
object AbsoluteLong : Mode {
|
||||||
|
override val canHaveLabel = true
|
||||||
|
override fun operandLength(state: State) = 3u
|
||||||
|
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.long)
|
||||||
|
override fun referencedAddress(ins: CodeUnit) = ins.memory.toCanonical(SnesAddress(ins.long))
|
||||||
|
}
|
||||||
|
|
||||||
|
private object ImmediateUnknownMode : Mode {
|
||||||
|
override val canHaveLabel = false
|
||||||
|
override fun operandLength(state: State): UInt? = null
|
||||||
|
override fun printRaw(ins: CodeUnit) = "???"
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
object ImmediateM : MultiMode(ImmediateUnknownMode, Immediate8, Immediate16) {
|
||||||
|
override fun pickMode(state: State) = state.mWidth ?: 0u
|
||||||
|
}
|
||||||
|
|
||||||
|
object ImmediateX : MultiMode(ImmediateUnknownMode, Immediate8, Immediate16) {
|
||||||
|
override fun pickMode(state: State) = state.xWidth ?: 0u
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BaseRelativeMode(private val length: UInt) : Mode {
|
||||||
|
override val showLengthSuffix = false
|
||||||
|
override fun operandLength(state: State) = length
|
||||||
|
override fun printRaw(ins: CodeUnit) = "$" + referencedAddress(ins).toSimpleString()
|
||||||
|
override fun referencedAddress(ins: CodeUnit) = ins.memory.toCanonical(ins.relativeAddress + (relativeOffset(ins) + 1 + length.toInt()))
|
||||||
|
protected abstract fun relativeOffset(ins: CodeUnit): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
object Relative : BaseRelativeMode(1u) {
|
||||||
|
override val canHaveLabel = true
|
||||||
|
override fun relativeOffset(ins: CodeUnit) = ins.signedByte.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
object RelativeLong : BaseRelativeMode(2u) {
|
||||||
|
override val canHaveLabel = true
|
||||||
|
override fun relativeOffset(ins: CodeUnit) = ins.signedWord.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
object BlockMove : Mode {
|
||||||
|
override val canHaveLabel = false
|
||||||
|
override val showLengthSuffix = false
|
||||||
|
|
||||||
|
override fun operandLength(state: State) = 2u
|
||||||
|
|
||||||
|
override fun printRaw(ins: CodeUnit) = String.format("#$%02x,#$%02x", ins.byte.toInt(), ins.byte2.toInt())
|
||||||
|
|
||||||
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private class IndexXMode(parent: Mode) : WrappedMode(parent, suffix = ",x")
|
||||||
|
private class IndexYMode(parent: Mode) : WrappedMode(parent, suffix = ",y")
|
||||||
|
private class IndexSMode(parent: Mode) : RawWrappedMode(parent, suffix = ",s")
|
||||||
|
private class IndirectMode(parent: Mode) : WrappedMode(parent, "(", ")")
|
||||||
|
private class IndirectLongMode(parent: Mode) : WrappedMode(parent, "[", "]")
|
||||||
|
|
|
@ -3,7 +3,6 @@ package com.smallhacker.disbrowser.asm
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.asm.Mnemonic.*
|
import com.smallhacker.disbrowser.asm.Mnemonic.*
|
||||||
import com.smallhacker.disbrowser.asm.Mode.*
|
|
||||||
|
|
||||||
typealias SegmentEnder = Instruction.() -> SegmentEnd?
|
typealias SegmentEnder = Instruction.() -> SegmentEnd?
|
||||||
|
|
||||||
|
@ -46,12 +45,13 @@ class Opcode private constructor(val mnemonic: Mnemonic, val mode: Mode, val end
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val DATA_BYTE = Opcode(Mnemonic.DB, Mode.DATA_BYTE, { null }, { it.preState })
|
val DATA_BYTE = Opcode(Mnemonic.DB, DirectData.byte, { null }, { it.preState })
|
||||||
val DATA_WORD = Opcode(Mnemonic.DW, Mode.DATA_WORD, { null }, { it.preState })
|
val DATA_WORD = Opcode(Mnemonic.DW, DirectData.word, { null }, { it.preState })
|
||||||
val DATA_LONG = Opcode(Mnemonic.DL, Mode.DATA_LONG, { null }, { it.preState })
|
val DATA_LONG = Opcode(Mnemonic.DL, DirectData.long, { null }, { it.preState })
|
||||||
|
val DATA_POINTER_WORD = Opcode(Mnemonic.DW, DataPointer.word, { null }, { it.preState }).linking()
|
||||||
val POINTER_WORD = Opcode(Mnemonic.DW, Mode.CODE_WORD, { null }, { it.preState }).linking()
|
val DATA_POINTER_LONG = Opcode(Mnemonic.DL, DataPointer.long, { null }, { it.preState }).linking()
|
||||||
val POINTER_LONG = Opcode(Mnemonic.DL, Mode.CODE_LONG, { null }, { it.preState }).linking()
|
val CODE_POINTER_WORD = Opcode(Mnemonic.DW, CodePointer.word, { null }, { it.preState }).linking()
|
||||||
|
val CODE_POINTER_LONG = Opcode(Mnemonic.DL, CodePointer.long, { null }, { it.preState }).linking()
|
||||||
|
|
||||||
val UNKNOWN_OPCODE: Opcode
|
val UNKNOWN_OPCODE: Opcode
|
||||||
|
|
||||||
|
@ -80,284 +80,284 @@ class Opcode private constructor(val mnemonic: Mnemonic, val mode: Mode, val end
|
||||||
val dynamicSubJumping: SegmentEnder = { stoppingSegmentEnd(address) }
|
val dynamicSubJumping: SegmentEnder = { stoppingSegmentEnd(address) }
|
||||||
val returning: SegmentEnder = { returnSegmentEnd(address) }
|
val returning: SegmentEnder = { returnSegmentEnd(address) }
|
||||||
|
|
||||||
UNKNOWN_OPCODE = Opcode(UNKNOWN, IMPLIED, alwaysStop, Instruction::preState).stop()
|
UNKNOWN_OPCODE = Opcode(UNKNOWN, Implied, alwaysStop, Instruction::preState).stop()
|
||||||
|
|
||||||
add(0x00, BRK, IMMEDIATE_8, alwaysStop).stop()
|
add(0x00, BRK, Immediate8, alwaysStop).stop()
|
||||||
add(0x02, COP, IMMEDIATE_8, alwaysStop).stop()
|
add(0x02, COP, Immediate8, alwaysStop).stop()
|
||||||
add(0x42, WDM, IMMEDIATE_8, alwaysStop).stop()
|
add(0x42, WDM, Immediate8, alwaysStop).stop()
|
||||||
|
|
||||||
add(0xEA, NOP, IMPLIED, alwaysContinue)
|
add(0xEA, NOP, Implied, alwaysContinue)
|
||||||
|
|
||||||
add(0xDB, STP, IMPLIED, alwaysStop).stop()
|
add(0xDB, STP, Implied, alwaysStop).stop()
|
||||||
add(0xCB, WAI, IMPLIED, alwaysContinue)
|
add(0xCB, WAI, Implied, alwaysContinue)
|
||||||
|
|
||||||
add(0x10, BPL, RELATIVE, branching).branching()
|
add(0x10, BPL, Relative, branching).branching()
|
||||||
add(0x30, BMI, RELATIVE, branching).branching()
|
add(0x30, BMI, Relative, branching).branching()
|
||||||
add(0x50, BVC, RELATIVE, branching).branching()
|
add(0x50, BVC, Relative, branching).branching()
|
||||||
add(0x70, BVS, RELATIVE, branching).branching()
|
add(0x70, BVS, Relative, branching).branching()
|
||||||
add(0x80, BRA, RELATIVE, alwaysBranching).stop().branching()
|
add(0x80, BRA, Relative, alwaysBranching).stop().branching()
|
||||||
add(0x90, BCC, RELATIVE, branching).branching()
|
add(0x90, BCC, Relative, branching).branching()
|
||||||
add(0xB0, BCS, RELATIVE, branching).branching()
|
add(0xB0, BCS, Relative, branching).branching()
|
||||||
add(0xD0, BNE, RELATIVE, branching).branching()
|
add(0xD0, BNE, Relative, branching).branching()
|
||||||
add(0xF0, BEQ, RELATIVE, branching).branching()
|
add(0xF0, BEQ, Relative, branching).branching()
|
||||||
add(0x82, BRL, RELATIVE_LONG, alwaysBranching).stop().branching()
|
add(0x82, BRL, RelativeLong, alwaysBranching).stop().branching()
|
||||||
|
|
||||||
add(0x4C, JMP, ABSOLUTE_CODE, jumping).linking().stop()
|
add(0x4C, JMP, AbsoluteCode, jumping).linking().stop()
|
||||||
add(0x5C, JML, ABSOLUTE_LONG, jumping).linking().stop()
|
add(0x5C, JML, AbsoluteLong, jumping).linking().stop()
|
||||||
add(0x6C, JMP, ABSOLUTE_INDIRECT, dynamicJumping).stop()
|
add(0x6C, JMP, Absolute.indirect, dynamicJumping).stop()
|
||||||
add(0x7C, JMP, ABSOLUTE_X_INDIRECT, dynamicJumping).stop()
|
add(0x7C, JMP, Absolute.x.indirect, dynamicJumping).stop()
|
||||||
add(0xDC, JMP, ABSOLUTE_INDIRECT_LONG, dynamicJumping).stop()
|
add(0xDC, JMP, Absolute.indirectLong, dynamicJumping).stop()
|
||||||
|
|
||||||
add(0x22, JSL, ABSOLUTE_LONG, subJumping).linking().mayStop()
|
add(0x22, JSL, AbsoluteLong, subJumping).linking().mayStop()
|
||||||
add(0x20, JSR, ABSOLUTE_CODE, subJumping).linking().mayStop()
|
add(0x20, JSR, AbsoluteCode, subJumping).linking().mayStop()
|
||||||
add(0xFC, JSR, ABSOLUTE_X_INDIRECT, dynamicSubJumping).mayStop()
|
add(0xFC, JSR, Absolute.x.indirect, dynamicSubJumping).mayStop()
|
||||||
|
|
||||||
add(0x60, RTS, IMPLIED, returning).stop()
|
add(0x60, RTS, Implied, returning).stop()
|
||||||
add(0x6B, RTL, IMPLIED, returning).stop()
|
add(0x6B, RTL, Implied, returning).stop()
|
||||||
add(0x40, RTI, IMPLIED, returning).stop()
|
add(0x40, RTI, Implied, returning).stop()
|
||||||
|
|
||||||
add(0x1B, TCS, IMPLIED, alwaysContinue)
|
add(0x1B, TCS, Implied, alwaysContinue)
|
||||||
add(0x3B, TSC, IMPLIED, alwaysContinue)
|
add(0x3B, TSC, Implied, alwaysContinue)
|
||||||
add(0x5B, TCD, IMPLIED, alwaysContinue)
|
add(0x5B, TCD, Implied, alwaysContinue)
|
||||||
add(0x7B, TDC, IMPLIED, alwaysContinue)
|
add(0x7B, TDC, Implied, alwaysContinue)
|
||||||
add(0xAA, TAX, IMPLIED, alwaysContinue)
|
add(0xAA, TAX, Implied, alwaysContinue)
|
||||||
add(0xA8, TAY, IMPLIED, alwaysContinue)
|
add(0xA8, TAY, Implied, alwaysContinue)
|
||||||
add(0xBA, TSX, IMPLIED, alwaysContinue)
|
add(0xBA, TSX, Implied, alwaysContinue)
|
||||||
add(0x8A, TXA, IMPLIED, alwaysContinue)
|
add(0x8A, TXA, Implied, alwaysContinue)
|
||||||
add(0x9A, TXS, IMPLIED, alwaysContinue)
|
add(0x9A, TXS, Implied, alwaysContinue)
|
||||||
add(0x9B, TXY, IMPLIED, alwaysContinue)
|
add(0x9B, TXY, Implied, alwaysContinue)
|
||||||
add(0x98, TYA, IMPLIED, alwaysContinue)
|
add(0x98, TYA, Implied, alwaysContinue)
|
||||||
add(0xBB, TYX, IMPLIED, alwaysContinue)
|
add(0xBB, TYX, Implied, alwaysContinue)
|
||||||
add(0xEB, XBA, IMPLIED, alwaysContinue)
|
add(0xEB, XBA, Implied, alwaysContinue)
|
||||||
|
|
||||||
add(0x18, CLC, IMPLIED, alwaysContinue)
|
add(0x18, CLC, Implied, alwaysContinue)
|
||||||
add(0x38, SEC, IMPLIED, alwaysContinue)
|
add(0x38, SEC, Implied, alwaysContinue)
|
||||||
add(0x58, CLI, IMPLIED, alwaysContinue)
|
add(0x58, CLI, Implied, alwaysContinue)
|
||||||
add(0x78, SEI, IMPLIED, alwaysContinue)
|
add(0x78, SEI, Implied, alwaysContinue)
|
||||||
add(0xF8, SED, IMPLIED, alwaysContinue)
|
add(0xF8, SED, Implied, alwaysContinue)
|
||||||
add(0xD8, CLD, IMPLIED, alwaysContinue)
|
add(0xD8, CLD, Implied, alwaysContinue)
|
||||||
add(0xB8, CLV, IMPLIED, alwaysContinue)
|
add(0xB8, CLV, Implied, alwaysContinue)
|
||||||
add(0xE2, SEP, IMMEDIATE_8, alwaysContinue) { it.preState.sep(it.bytes[1u]) }
|
add(0xE2, SEP, Immediate8, alwaysContinue) { it.preState.sep(it.bytes[1u]) }
|
||||||
add(0xC2, REP, IMMEDIATE_8, alwaysContinue) { it.preState.rep(it.bytes[1u]) }
|
add(0xC2, REP, Immediate8, alwaysContinue) { it.preState.rep(it.bytes[1u]) }
|
||||||
add(0xFB, XCE, IMPLIED, alwaysContinue)
|
add(0xFB, XCE, Implied, alwaysContinue)
|
||||||
|
|
||||||
add(0xC1, CMP, DIRECT_X_INDIRECT, alwaysContinue)
|
add(0xC1, CMP, Direct.x.indirect, alwaysContinue)
|
||||||
add(0xC3, CMP, DIRECT_S, alwaysContinue)
|
add(0xC3, CMP, Direct.s, alwaysContinue)
|
||||||
add(0xC5, CMP, DIRECT, alwaysContinue)
|
add(0xC5, CMP, Direct, alwaysContinue)
|
||||||
add(0xC7, CMP, DIRECT_INDIRECT_LONG, alwaysContinue)
|
add(0xC7, CMP, Direct.indirectLong, alwaysContinue)
|
||||||
add(0xC9, CMP, IMMEDIATE_M, alwaysContinue)
|
add(0xC9, CMP, ImmediateM, alwaysContinue)
|
||||||
add(0xCD, CMP, ABSOLUTE, alwaysContinue)
|
add(0xCD, CMP, Absolute, alwaysContinue)
|
||||||
add(0xCF, CMP, ABSOLUTE_LONG, alwaysContinue)
|
add(0xCF, CMP, AbsoluteLong, alwaysContinue)
|
||||||
add(0xD1, CMP, DIRECT_INDIRECT_Y, alwaysContinue)
|
add(0xD1, CMP, Direct.indirect.y, alwaysContinue)
|
||||||
add(0xD2, CMP, DIRECT_INDIRECT, alwaysContinue)
|
add(0xD2, CMP, Direct.indirect, alwaysContinue)
|
||||||
add(0xD3, CMP, DIRECT_S_INDIRECT_Y, alwaysContinue)
|
add(0xD3, CMP, Direct.s.indirect.y, alwaysContinue)
|
||||||
add(0xD5, CMP, DIRECT_X, alwaysContinue)
|
add(0xD5, CMP, Direct.x, alwaysContinue)
|
||||||
add(0xD7, CMP, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
|
add(0xD7, CMP, Direct.indirectLong.y, alwaysContinue)
|
||||||
add(0xD9, CMP, ABSOLUTE_Y, alwaysContinue)
|
add(0xD9, CMP, Absolute.y, alwaysContinue)
|
||||||
add(0xDD, CMP, ABSOLUTE_X, alwaysContinue)
|
add(0xDD, CMP, Absolute.x, alwaysContinue)
|
||||||
add(0xDF, CMP, ABSOLUTE_LONG_X, alwaysContinue)
|
add(0xDF, CMP, AbsoluteLong.x, alwaysContinue)
|
||||||
add(0xE0, CPX, IMMEDIATE_X, alwaysContinue)
|
add(0xE0, CPX, ImmediateX, alwaysContinue)
|
||||||
add(0xE4, CPX, DIRECT, alwaysContinue)
|
add(0xE4, CPX, Direct, alwaysContinue)
|
||||||
add(0xEC, CPX, ABSOLUTE, alwaysContinue)
|
add(0xEC, CPX, Absolute, alwaysContinue)
|
||||||
add(0xC0, CPY, IMMEDIATE_X, alwaysContinue)
|
add(0xC0, CPY, ImmediateX, alwaysContinue)
|
||||||
add(0xC4, CPY, DIRECT, alwaysContinue)
|
add(0xC4, CPY, Direct, alwaysContinue)
|
||||||
add(0xCC, CPY, ABSOLUTE, alwaysContinue)
|
add(0xCC, CPY, Absolute, alwaysContinue)
|
||||||
|
|
||||||
add(0xA1, LDA, DIRECT_X_INDIRECT, alwaysContinue)
|
add(0xA1, LDA, Direct.x.indirect, alwaysContinue)
|
||||||
add(0xA3, LDA, DIRECT_S, alwaysContinue)
|
add(0xA3, LDA, Direct.s, alwaysContinue)
|
||||||
add(0xA5, LDA, DIRECT, alwaysContinue)
|
add(0xA5, LDA, Direct, alwaysContinue)
|
||||||
add(0xA7, LDA, DIRECT_INDIRECT_LONG, alwaysContinue)
|
add(0xA7, LDA, Direct.indirectLong, alwaysContinue)
|
||||||
add(0xA9, LDA, IMMEDIATE_M, alwaysContinue)
|
add(0xA9, LDA, ImmediateM, alwaysContinue)
|
||||||
add(0xAD, LDA, ABSOLUTE, alwaysContinue)
|
add(0xAD, LDA, Absolute, alwaysContinue)
|
||||||
add(0xAF, LDA, ABSOLUTE_LONG, alwaysContinue)
|
add(0xAF, LDA, AbsoluteLong, alwaysContinue)
|
||||||
add(0xB1, LDA, DIRECT_INDIRECT_Y, alwaysContinue)
|
add(0xB1, LDA, Direct.indirect.y, alwaysContinue)
|
||||||
add(0xB2, LDA, DIRECT_INDIRECT, alwaysContinue)
|
add(0xB2, LDA, Direct.indirect, alwaysContinue)
|
||||||
add(0xB3, LDA, DIRECT_S_INDIRECT_Y, alwaysContinue)
|
add(0xB3, LDA, Direct.s.indirect.y, alwaysContinue)
|
||||||
add(0xB5, LDA, DIRECT_X, alwaysContinue)
|
add(0xB5, LDA, Direct.x, alwaysContinue)
|
||||||
add(0xB7, LDA, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
|
add(0xB7, LDA, Direct.indirectLong.y, alwaysContinue)
|
||||||
add(0xB9, LDA, ABSOLUTE_Y, alwaysContinue)
|
add(0xB9, LDA, Absolute.y, alwaysContinue)
|
||||||
add(0xBD, LDA, ABSOLUTE_X, alwaysContinue)
|
add(0xBD, LDA, Absolute.x, alwaysContinue)
|
||||||
add(0xBF, LDA, ABSOLUTE_LONG_X, alwaysContinue)
|
add(0xBF, LDA, AbsoluteLong.x, alwaysContinue)
|
||||||
add(0xA2, LDX, IMMEDIATE_X, alwaysContinue)
|
add(0xA2, LDX, ImmediateX, alwaysContinue)
|
||||||
add(0xA6, LDX, DIRECT, alwaysContinue)
|
add(0xA6, LDX, Direct, alwaysContinue)
|
||||||
add(0xAE, LDX, ABSOLUTE, alwaysContinue)
|
add(0xAE, LDX, Absolute, alwaysContinue)
|
||||||
add(0xB6, LDX, DIRECT_Y, alwaysContinue)
|
add(0xB6, LDX, Direct.y, alwaysContinue)
|
||||||
add(0xBE, LDX, ABSOLUTE_Y, alwaysContinue)
|
add(0xBE, LDX, Absolute.y, alwaysContinue)
|
||||||
add(0xA0, LDY, IMMEDIATE_X, alwaysContinue)
|
add(0xA0, LDY, ImmediateX, alwaysContinue)
|
||||||
add(0xA4, LDY, DIRECT, alwaysContinue)
|
add(0xA4, LDY, Direct, alwaysContinue)
|
||||||
add(0xAC, LDY, ABSOLUTE, alwaysContinue)
|
add(0xAC, LDY, Absolute, alwaysContinue)
|
||||||
add(0xB4, LDY, DIRECT_X, alwaysContinue)
|
add(0xB4, LDY, Direct.x, alwaysContinue)
|
||||||
add(0xBC, LDY, ABSOLUTE_X, alwaysContinue)
|
add(0xBC, LDY, Absolute.x, alwaysContinue)
|
||||||
add(0x81, STA, DIRECT_X_INDIRECT, alwaysContinue)
|
add(0x81, STA, Direct.x.indirect, alwaysContinue)
|
||||||
add(0x83, STA, DIRECT_S, alwaysContinue)
|
add(0x83, STA, Direct.s, alwaysContinue)
|
||||||
add(0x85, STA, DIRECT, alwaysContinue)
|
add(0x85, STA, Direct, alwaysContinue)
|
||||||
add(0x87, STA, DIRECT_INDIRECT_LONG, alwaysContinue)
|
add(0x87, STA, Direct.indirectLong, alwaysContinue)
|
||||||
add(0x8D, STA, ABSOLUTE, alwaysContinue)
|
add(0x8D, STA, Absolute, alwaysContinue)
|
||||||
add(0x8F, STA, ABSOLUTE_LONG, alwaysContinue)
|
add(0x8F, STA, AbsoluteLong, alwaysContinue)
|
||||||
add(0x91, STA, DIRECT_INDIRECT_Y, alwaysContinue)
|
add(0x91, STA, Direct.indirect.y, alwaysContinue)
|
||||||
add(0x92, STA, DIRECT_INDIRECT, alwaysContinue)
|
add(0x92, STA, Direct.indirect, alwaysContinue)
|
||||||
add(0x93, STA, DIRECT_S_INDIRECT_Y, alwaysContinue)
|
add(0x93, STA, Direct.s.indirect.y, alwaysContinue)
|
||||||
add(0x95, STA, DIRECT_X, alwaysContinue)
|
add(0x95, STA, Direct.x, alwaysContinue)
|
||||||
add(0x97, STA, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
|
add(0x97, STA, Direct.indirectLong.y, alwaysContinue)
|
||||||
add(0x99, STA, ABSOLUTE_Y, alwaysContinue)
|
add(0x99, STA, Absolute.y, alwaysContinue)
|
||||||
add(0x9D, STA, ABSOLUTE_X, alwaysContinue)
|
add(0x9D, STA, Absolute.x, alwaysContinue)
|
||||||
add(0x9F, STA, ABSOLUTE_LONG_X, alwaysContinue)
|
add(0x9F, STA, AbsoluteLong.x, alwaysContinue)
|
||||||
add(0x86, STX, DIRECT, alwaysContinue)
|
add(0x86, STX, Direct, alwaysContinue)
|
||||||
add(0x8E, STX, ABSOLUTE, alwaysContinue)
|
add(0x8E, STX, Absolute, alwaysContinue)
|
||||||
add(0x96, STX, DIRECT_Y, alwaysContinue)
|
add(0x96, STX, Direct.y, alwaysContinue)
|
||||||
add(0x84, STY, DIRECT, alwaysContinue)
|
add(0x84, STY, Direct, alwaysContinue)
|
||||||
add(0x8C, STY, ABSOLUTE, alwaysContinue)
|
add(0x8C, STY, Absolute, alwaysContinue)
|
||||||
add(0x94, STY, DIRECT_X, alwaysContinue)
|
add(0x94, STY, Direct.x, alwaysContinue)
|
||||||
add(0x64, STZ, DIRECT, alwaysContinue)
|
add(0x64, STZ, Direct, alwaysContinue)
|
||||||
add(0x74, STZ, DIRECT_X, alwaysContinue)
|
add(0x74, STZ, Direct.x, alwaysContinue)
|
||||||
add(0x9C, STZ, ABSOLUTE, alwaysContinue)
|
add(0x9C, STZ, Absolute, alwaysContinue)
|
||||||
add(0x9E, STZ, ABSOLUTE_X, alwaysContinue)
|
add(0x9E, STZ, Absolute.x, alwaysContinue)
|
||||||
|
|
||||||
add(0x48, PHA, IMPLIED, alwaysContinue) { it.preState.pushUnknown(it.preState.mWidth) }
|
add(0x48, PHA, Implied, alwaysContinue) { it.preState.pushUnknown(it.preState.mWidth) }
|
||||||
add(0xDA, PHX, IMPLIED, alwaysContinue) { it.preState.pushUnknown(it.preState.xWidth) }
|
add(0xDA, PHX, Implied, alwaysContinue) { it.preState.pushUnknown(it.preState.xWidth) }
|
||||||
add(0x5A, PHY, 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(0x68, PLA, Implied, alwaysContinue) { it.preState.pull(it.preState.mWidth) }
|
||||||
add(0xFA, PLX, IMPLIED, alwaysContinue) { it.preState.pull(it.preState.xWidth) }
|
add(0xFA, PLX, Implied, alwaysContinue) { it.preState.pull(it.preState.xWidth) }
|
||||||
add(0x7A, PLY, 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(1u) }
|
add(0x8B, PHB, Implied, alwaysContinue) { it.preState.pushUnknown(1u) }
|
||||||
add(0xAB, PLB, IMPLIED, alwaysContinue) { it.preState.pull(1u) }
|
add(0xAB, PLB, Implied, alwaysContinue) { it.preState.pull(1u) }
|
||||||
add(0x0B, PHD, IMPLIED, alwaysContinue) { it.preState.pushUnknown(1u) }
|
add(0x0B, PHD, Implied, alwaysContinue) { it.preState.pushUnknown(1u) }
|
||||||
add(0x2B, PLD, IMPLIED, alwaysContinue) { it.preState.pull(1u) }
|
add(0x2B, PLD, Implied, alwaysContinue) { it.preState.pull(1u) }
|
||||||
add(0x4B, PHK, IMPLIED, alwaysContinue) { it.preState.push((it.address.value shr 16).toUInt()) }
|
add(0x4B, PHK, Implied, alwaysContinue) { it.preState.push((it.address.value shr 16).toUInt()) }
|
||||||
add(0x08, PHP, IMPLIED, alwaysContinue) { it.preState.push(it.preState.flags) }
|
add(0x08, PHP, Implied, alwaysContinue) { it.preState.push(it.preState.flags) }
|
||||||
add(0x28, PLP, IMPLIED, alwaysContinue) { it.preState.pull { copy(flags = it) } }
|
add(0x28, PLP, Implied, alwaysContinue) { it.preState.pull { copy(flags = it) } }
|
||||||
|
|
||||||
add(0x3A, DEC, IMPLIED, alwaysContinue)
|
add(0x3A, DEC, Implied, alwaysContinue)
|
||||||
add(0xC6, DEC, DIRECT, alwaysContinue)
|
add(0xC6, DEC, Direct, alwaysContinue)
|
||||||
add(0xCE, DEC, ABSOLUTE, alwaysContinue)
|
add(0xCE, DEC, Absolute, alwaysContinue)
|
||||||
add(0xD6, DEC, DIRECT_X, alwaysContinue)
|
add(0xD6, DEC, Direct.x, alwaysContinue)
|
||||||
add(0xDE, DEC, ABSOLUTE_X, alwaysContinue)
|
add(0xDE, DEC, Absolute.x, alwaysContinue)
|
||||||
add(0xCA, DEX, IMPLIED, alwaysContinue)
|
add(0xCA, DEX, Implied, alwaysContinue)
|
||||||
add(0x88, DEY, IMPLIED, alwaysContinue)
|
add(0x88, DEY, Implied, alwaysContinue)
|
||||||
add(0x1A, INC, IMPLIED, alwaysContinue)
|
add(0x1A, INC, Implied, alwaysContinue)
|
||||||
add(0xE6, INC, DIRECT, alwaysContinue)
|
add(0xE6, INC, Direct, alwaysContinue)
|
||||||
add(0xEE, INC, ABSOLUTE, alwaysContinue)
|
add(0xEE, INC, Absolute, alwaysContinue)
|
||||||
add(0xF6, INC, DIRECT_X, alwaysContinue)
|
add(0xF6, INC, Direct.x, alwaysContinue)
|
||||||
add(0xFE, INC, ABSOLUTE_X, alwaysContinue)
|
add(0xFE, INC, Absolute.x, alwaysContinue)
|
||||||
add(0xE8, INX, IMPLIED, alwaysContinue)
|
add(0xE8, INX, Implied, alwaysContinue)
|
||||||
add(0xC8, INY, IMPLIED, alwaysContinue)
|
add(0xC8, INY, Implied, alwaysContinue)
|
||||||
|
|
||||||
add(0x06, ASL, DIRECT, alwaysContinue)
|
add(0x06, ASL, Direct, alwaysContinue)
|
||||||
add(0x0A, ASL, IMPLIED, alwaysContinue)
|
add(0x0A, ASL, Implied, alwaysContinue)
|
||||||
add(0x0E, ASL, ABSOLUTE, alwaysContinue)
|
add(0x0E, ASL, Absolute, alwaysContinue)
|
||||||
add(0x16, ASL, DIRECT_X, alwaysContinue)
|
add(0x16, ASL, Direct.x, alwaysContinue)
|
||||||
add(0x1E, ASL, ABSOLUTE_X, alwaysContinue)
|
add(0x1E, ASL, Absolute.x, alwaysContinue)
|
||||||
add(0x46, LSR, DIRECT, alwaysContinue)
|
add(0x46, LSR, Direct, alwaysContinue)
|
||||||
add(0x4A, LSR, IMPLIED, alwaysContinue)
|
add(0x4A, LSR, Implied, alwaysContinue)
|
||||||
add(0x4E, LSR, ABSOLUTE, alwaysContinue)
|
add(0x4E, LSR, Absolute, alwaysContinue)
|
||||||
add(0x56, LSR, DIRECT_X, alwaysContinue)
|
add(0x56, LSR, Direct.x, alwaysContinue)
|
||||||
add(0x5E, LSR, ABSOLUTE_X, alwaysContinue)
|
add(0x5E, LSR, Absolute.x, alwaysContinue)
|
||||||
add(0x26, ROL, DIRECT, alwaysContinue)
|
add(0x26, ROL, Direct, alwaysContinue)
|
||||||
add(0x2A, ROL, IMPLIED, alwaysContinue)
|
add(0x2A, ROL, Implied, alwaysContinue)
|
||||||
add(0x2E, ROL, ABSOLUTE, alwaysContinue)
|
add(0x2E, ROL, Absolute, alwaysContinue)
|
||||||
add(0x36, ROL, DIRECT_X, alwaysContinue)
|
add(0x36, ROL, Direct.x, alwaysContinue)
|
||||||
add(0x3E, ROL, ABSOLUTE_X, alwaysContinue)
|
add(0x3E, ROL, Absolute.x, alwaysContinue)
|
||||||
add(0x66, ROR, DIRECT, alwaysContinue)
|
add(0x66, ROR, Direct, alwaysContinue)
|
||||||
add(0x6A, ROR, IMPLIED, alwaysContinue)
|
add(0x6A, ROR, Implied, alwaysContinue)
|
||||||
add(0x6E, ROR, ABSOLUTE, alwaysContinue)
|
add(0x6E, ROR, Absolute, alwaysContinue)
|
||||||
add(0x76, ROR, DIRECT_X, alwaysContinue)
|
add(0x76, ROR, Direct.x, alwaysContinue)
|
||||||
add(0x7E, ROR, ABSOLUTE_X, alwaysContinue)
|
add(0x7E, ROR, Absolute.x, alwaysContinue)
|
||||||
|
|
||||||
add(0x61, ADC, DIRECT_X_INDIRECT, alwaysContinue)
|
add(0x61, ADC, Direct.x.indirect, alwaysContinue)
|
||||||
add(0x63, ADC, DIRECT_S, alwaysContinue)
|
add(0x63, ADC, Direct.s, alwaysContinue)
|
||||||
add(0x65, ADC, DIRECT, alwaysContinue)
|
add(0x65, ADC, Direct, alwaysContinue)
|
||||||
add(0x67, ADC, DIRECT_INDIRECT_LONG, alwaysContinue)
|
add(0x67, ADC, Direct.indirectLong, alwaysContinue)
|
||||||
add(0x69, ADC, IMMEDIATE_M, alwaysContinue)
|
add(0x69, ADC, ImmediateM, alwaysContinue)
|
||||||
add(0x6D, ADC, ABSOLUTE, alwaysContinue)
|
add(0x6D, ADC, Absolute, alwaysContinue)
|
||||||
add(0x6F, ADC, ABSOLUTE_LONG, alwaysContinue)
|
add(0x6F, ADC, AbsoluteLong, alwaysContinue)
|
||||||
add(0x71, ADC, DIRECT_INDIRECT_Y, alwaysContinue)
|
add(0x71, ADC, Direct.indirect.y, alwaysContinue)
|
||||||
add(0x72, ADC, DIRECT_INDIRECT, alwaysContinue)
|
add(0x72, ADC, Direct.indirect, alwaysContinue)
|
||||||
add(0x73, ADC, DIRECT_S_INDIRECT_Y, alwaysContinue)
|
add(0x73, ADC, Direct.s.indirect.y, alwaysContinue)
|
||||||
add(0x75, ADC, DIRECT_X, alwaysContinue)
|
add(0x75, ADC, Direct.x, alwaysContinue)
|
||||||
add(0x77, ADC, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
|
add(0x77, ADC, Direct.indirectLong.y, alwaysContinue)
|
||||||
add(0x79, ADC, ABSOLUTE_Y, alwaysContinue)
|
add(0x79, ADC, Absolute.y, alwaysContinue)
|
||||||
add(0x7D, ADC, ABSOLUTE_X, alwaysContinue)
|
add(0x7D, ADC, Absolute.x, alwaysContinue)
|
||||||
add(0x7F, ADC, ABSOLUTE_LONG_X, alwaysContinue)
|
add(0x7F, ADC, AbsoluteLong.x, alwaysContinue)
|
||||||
add(0xE1, SBC, DIRECT_X_INDIRECT, alwaysContinue)
|
add(0xE1, SBC, Direct.x.indirect, alwaysContinue)
|
||||||
add(0xE3, SBC, DIRECT_S, alwaysContinue)
|
add(0xE3, SBC, Direct.s, alwaysContinue)
|
||||||
add(0xE5, SBC, DIRECT, alwaysContinue)
|
add(0xE5, SBC, Direct, alwaysContinue)
|
||||||
add(0xE7, SBC, DIRECT_INDIRECT_LONG, alwaysContinue)
|
add(0xE7, SBC, Direct.indirectLong, alwaysContinue)
|
||||||
add(0xE9, SBC, IMMEDIATE_M, alwaysContinue)
|
add(0xE9, SBC, ImmediateM, alwaysContinue)
|
||||||
add(0xED, SBC, ABSOLUTE, alwaysContinue)
|
add(0xED, SBC, Absolute, alwaysContinue)
|
||||||
add(0xEF, SBC, ABSOLUTE_LONG, alwaysContinue)
|
add(0xEF, SBC, AbsoluteLong, alwaysContinue)
|
||||||
add(0xF1, SBC, DIRECT_INDIRECT_Y, alwaysContinue)
|
add(0xF1, SBC, Direct.indirect.y, alwaysContinue)
|
||||||
add(0xF2, SBC, DIRECT_INDIRECT, alwaysContinue)
|
add(0xF2, SBC, Direct.indirect, alwaysContinue)
|
||||||
add(0xF3, SBC, DIRECT_S_INDIRECT_Y, alwaysContinue)
|
add(0xF3, SBC, Direct.s.indirect.y, alwaysContinue)
|
||||||
add(0xF5, SBC, DIRECT_X, alwaysContinue)
|
add(0xF5, SBC, Direct.x, alwaysContinue)
|
||||||
add(0xF7, SBC, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
|
add(0xF7, SBC, Direct.indirectLong.y, alwaysContinue)
|
||||||
add(0xF9, SBC, ABSOLUTE_Y, alwaysContinue)
|
add(0xF9, SBC, Absolute.y, alwaysContinue)
|
||||||
add(0xFD, SBC, ABSOLUTE_X, alwaysContinue)
|
add(0xFD, SBC, Absolute.x, alwaysContinue)
|
||||||
add(0xFF, SBC, ABSOLUTE_LONG_X, alwaysContinue)
|
add(0xFF, SBC, AbsoluteLong.x, alwaysContinue)
|
||||||
|
|
||||||
add(0x21, AND, DIRECT_X_INDIRECT, alwaysContinue)
|
add(0x21, AND, Direct.x.indirect, alwaysContinue)
|
||||||
add(0x23, AND, DIRECT_S, alwaysContinue)
|
add(0x23, AND, Direct.s, alwaysContinue)
|
||||||
add(0x25, AND, DIRECT, alwaysContinue)
|
add(0x25, AND, Direct, alwaysContinue)
|
||||||
add(0x27, AND, DIRECT_INDIRECT_LONG, alwaysContinue)
|
add(0x27, AND, Direct.indirectLong, alwaysContinue)
|
||||||
add(0x29, AND, IMMEDIATE_M, alwaysContinue)
|
add(0x29, AND, ImmediateM, alwaysContinue)
|
||||||
add(0x2D, AND, ABSOLUTE, alwaysContinue)
|
add(0x2D, AND, Absolute, alwaysContinue)
|
||||||
add(0x2F, AND, ABSOLUTE_LONG, alwaysContinue)
|
add(0x2F, AND, AbsoluteLong, alwaysContinue)
|
||||||
add(0x31, AND, DIRECT_INDIRECT_Y, alwaysContinue)
|
add(0x31, AND, Direct.indirect.y, alwaysContinue)
|
||||||
add(0x32, AND, DIRECT_INDIRECT, alwaysContinue)
|
add(0x32, AND, Direct.indirect, alwaysContinue)
|
||||||
add(0x33, AND, DIRECT_S_INDIRECT_Y, alwaysContinue)
|
add(0x33, AND, Direct.s.indirect.y, alwaysContinue)
|
||||||
add(0x35, AND, DIRECT_X, alwaysContinue)
|
add(0x35, AND, Direct.x, alwaysContinue)
|
||||||
add(0x37, AND, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
|
add(0x37, AND, Direct.indirectLong.y, alwaysContinue)
|
||||||
add(0x39, AND, ABSOLUTE_Y, alwaysContinue)
|
add(0x39, AND, Absolute.y, alwaysContinue)
|
||||||
add(0x3D, AND, ABSOLUTE_X, alwaysContinue)
|
add(0x3D, AND, Absolute.x, alwaysContinue)
|
||||||
add(0x3F, AND, ABSOLUTE_LONG_X, alwaysContinue)
|
add(0x3F, AND, AbsoluteLong.x, alwaysContinue)
|
||||||
add(0x41, EOR, DIRECT_X_INDIRECT, alwaysContinue)
|
add(0x41, EOR, Direct.x.indirect, alwaysContinue)
|
||||||
add(0x43, EOR, DIRECT_S, alwaysContinue)
|
add(0x43, EOR, Direct.s, alwaysContinue)
|
||||||
add(0x45, EOR, DIRECT, alwaysContinue)
|
add(0x45, EOR, Direct, alwaysContinue)
|
||||||
add(0x47, EOR, DIRECT_INDIRECT_LONG, alwaysContinue)
|
add(0x47, EOR, Direct.indirectLong, alwaysContinue)
|
||||||
add(0x49, EOR, IMMEDIATE_M, alwaysContinue)
|
add(0x49, EOR, ImmediateM, alwaysContinue)
|
||||||
add(0x4D, EOR, ABSOLUTE, alwaysContinue)
|
add(0x4D, EOR, Absolute, alwaysContinue)
|
||||||
add(0x4F, EOR, ABSOLUTE_LONG, alwaysContinue)
|
add(0x4F, EOR, AbsoluteLong, alwaysContinue)
|
||||||
add(0x51, EOR, DIRECT_INDIRECT_Y, alwaysContinue)
|
add(0x51, EOR, Direct.indirect.y, alwaysContinue)
|
||||||
add(0x52, EOR, DIRECT_INDIRECT, alwaysContinue)
|
add(0x52, EOR, Direct.indirect, alwaysContinue)
|
||||||
add(0x53, EOR, DIRECT_S_INDIRECT_Y, alwaysContinue)
|
add(0x53, EOR, Direct.s.indirect.y, alwaysContinue)
|
||||||
add(0x55, EOR, DIRECT_X, alwaysContinue)
|
add(0x55, EOR, Direct.x, alwaysContinue)
|
||||||
add(0x57, EOR, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
|
add(0x57, EOR, Direct.indirectLong.y, alwaysContinue)
|
||||||
add(0x59, EOR, ABSOLUTE_Y, alwaysContinue)
|
add(0x59, EOR, Absolute.y, alwaysContinue)
|
||||||
add(0x5D, EOR, ABSOLUTE_X, alwaysContinue)
|
add(0x5D, EOR, Absolute.x, alwaysContinue)
|
||||||
add(0x5F, EOR, ABSOLUTE_LONG_X, alwaysContinue)
|
add(0x5F, EOR, AbsoluteLong.x, alwaysContinue)
|
||||||
add(0x01, ORA, DIRECT_X_INDIRECT, alwaysContinue)
|
add(0x01, ORA, Direct.x.indirect, alwaysContinue)
|
||||||
add(0x03, ORA, DIRECT_S, alwaysContinue)
|
add(0x03, ORA, Direct.s, alwaysContinue)
|
||||||
add(0x05, ORA, DIRECT, alwaysContinue)
|
add(0x05, ORA, Direct, alwaysContinue)
|
||||||
add(0x07, ORA, DIRECT_INDIRECT_LONG, alwaysContinue)
|
add(0x07, ORA, Direct.indirectLong, alwaysContinue)
|
||||||
add(0x09, ORA, IMMEDIATE_M, alwaysContinue)
|
add(0x09, ORA, ImmediateM, alwaysContinue)
|
||||||
add(0x0D, ORA, ABSOLUTE, alwaysContinue)
|
add(0x0D, ORA, Absolute, alwaysContinue)
|
||||||
add(0x0F, ORA, ABSOLUTE_LONG, alwaysContinue)
|
add(0x0F, ORA, AbsoluteLong, alwaysContinue)
|
||||||
add(0x11, ORA, DIRECT_INDIRECT_Y, alwaysContinue)
|
add(0x11, ORA, Direct.indirect.y, alwaysContinue)
|
||||||
add(0x12, ORA, DIRECT_INDIRECT, alwaysContinue)
|
add(0x12, ORA, Direct.indirect, alwaysContinue)
|
||||||
add(0x13, ORA, DIRECT_S_INDIRECT_Y, alwaysContinue)
|
add(0x13, ORA, Direct.s.indirect.y, alwaysContinue)
|
||||||
add(0x15, ORA, DIRECT_X, alwaysContinue)
|
add(0x15, ORA, Direct.x, alwaysContinue)
|
||||||
add(0x17, ORA, DIRECT_INDIRECT_LONG_Y, alwaysContinue)
|
add(0x17, ORA, Direct.indirectLong.y, alwaysContinue)
|
||||||
add(0x19, ORA, ABSOLUTE_Y, alwaysContinue)
|
add(0x19, ORA, Absolute.y, alwaysContinue)
|
||||||
add(0x1D, ORA, ABSOLUTE_X, alwaysContinue)
|
add(0x1D, ORA, Absolute.x, alwaysContinue)
|
||||||
add(0x1F, ORA, ABSOLUTE_LONG_X, alwaysContinue)
|
add(0x1F, ORA, AbsoluteLong.x, alwaysContinue)
|
||||||
|
|
||||||
add(0x14, TRB, DIRECT, alwaysContinue)
|
add(0x14, TRB, Direct, alwaysContinue)
|
||||||
add(0x1C, TRB, ABSOLUTE, alwaysContinue)
|
add(0x1C, TRB, Absolute, alwaysContinue)
|
||||||
add(0x04, TSB, DIRECT, alwaysContinue)
|
add(0x04, TSB, Direct, alwaysContinue)
|
||||||
add(0x0C, TSB, ABSOLUTE, alwaysContinue)
|
add(0x0C, TSB, Absolute, alwaysContinue)
|
||||||
|
|
||||||
add(0x24, BIT, DIRECT, alwaysContinue)
|
add(0x24, BIT, Direct, alwaysContinue)
|
||||||
add(0x2C, BIT, ABSOLUTE, alwaysContinue)
|
add(0x2C, BIT, Absolute, alwaysContinue)
|
||||||
add(0x34, BIT, DIRECT_X, alwaysContinue)
|
add(0x34, BIT, Direct.x, alwaysContinue)
|
||||||
add(0x3C, BIT, ABSOLUTE_X, alwaysContinue)
|
add(0x3C, BIT, Absolute.x, alwaysContinue)
|
||||||
add(0x89, BIT, IMMEDIATE_M, alwaysContinue)
|
add(0x89, BIT, ImmediateM, alwaysContinue)
|
||||||
|
|
||||||
add(0x54, MVN, BLOCK_MOVE, alwaysContinue)
|
add(0x54, MVN, BlockMove, alwaysContinue)
|
||||||
add(0x44, MVP, BLOCK_MOVE, alwaysContinue)
|
add(0x44, MVP, BlockMove, alwaysContinue)
|
||||||
|
|
||||||
add(0xF4, PEA, IMMEDIATE_16, alwaysContinue)
|
add(0xF4, PEA, Immediate16, alwaysContinue)
|
||||||
add(0xD4, PEI, DIRECT, alwaysContinue)
|
add(0xD4, PEI, Direct, alwaysContinue)
|
||||||
add(0x62, PER, RELATIVE_LONG, alwaysContinue)
|
add(0x62, PER, RelativeLong, alwaysContinue)
|
||||||
|
|
||||||
OPCODES = Array(256) { ocs[it]!! }
|
OPCODES = Array(256) { ocs[it]!! }
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ abstract class SnesMapper: MemorySpace {
|
||||||
return entry.space[offset]
|
return entry.space[offset]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toCanonical(address: SnesAddress): SnesAddress? {
|
fun toCanonical(address: SnesAddress): SnesAddress {
|
||||||
val entry = areas[address.value.toUInt()] ?: return null
|
val entry = areas[address.value.toUInt()] ?: return address
|
||||||
val offset = address.value - entry.start
|
val offset = address.value - entry.start
|
||||||
return entry.canonicalStart + offset.toInt()
|
return entry.canonicalStart + offset.toInt()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import com.smallhacker.disbrowser.ImmStack
|
||||||
import com.smallhacker.disbrowser.immStack
|
import com.smallhacker.disbrowser.immStack
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.disbrowser.util.toUInt24
|
||||||
|
|
||||||
data class State(val origin: Instruction? = null, val memory: SnesMapper, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack()) {
|
data class State(val origin: Instruction? = null, val memory: SnesMapper, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack(), val metadata: Metadata) {
|
||||||
val m: Boolean? get() = flags.getBoolean(0x20u)
|
val m: Boolean? get() = flags.getBoolean(0x20u)
|
||||||
val x: Boolean? get() = flags.getBoolean(0x10u)
|
val x: Boolean? get() = flags.getBoolean(0x10u)
|
||||||
val db: UByte? get() = pb // TODO
|
val db: UByte? get() = pb // TODO
|
||||||
|
@ -58,17 +58,18 @@ data class State(val origin: Instruction? = null, val memory: SnesMapper, val ad
|
||||||
|
|
||||||
fun resolveDirectPage(directPage: UByte) = dp?.let { dp ->
|
fun resolveDirectPage(directPage: UByte) = dp?.let { dp ->
|
||||||
val ptr = (dp.toUInt24() shl 8) or (directPage.toUInt24())
|
val ptr = (dp.toUInt24() shl 8) or (directPage.toUInt24())
|
||||||
SnesAddress(ptr)
|
memory.toCanonical(SnesAddress(ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveAbsoluteData(absolute: UShort) = db?.let { db ->
|
fun resolveAbsoluteData(absolute: UShort) = db?.let { db ->
|
||||||
val ptr = (db.toUInt24() shl 16) or (absolute.toUInt24())
|
val ptr = (db.toUInt24() shl 16) or (absolute.toUInt24())
|
||||||
SnesAddress(ptr)
|
memory.toCanonical(SnesAddress(ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveAbsoluteCode(absolute: UShort): SnesAddress {
|
fun resolveAbsoluteCode(absolute: UShort): SnesAddress? {
|
||||||
val ptr = (pb.toUInt24() shl 16) or (absolute.toUInt24())
|
val ptr = (pb.toUInt24() shl 16) or (absolute.toUInt24())
|
||||||
return SnesAddress(ptr)
|
val address = SnesAddress(ptr)
|
||||||
|
return memory.toCanonical(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stackToString(): String {
|
private fun stackToString(): String {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.smallhacker.disbrowser.resource
|
package com.smallhacker.disbrowser.resource
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.HtmlNode
|
import com.smallhacker.disbrowser.*
|
||||||
import com.smallhacker.disbrowser.Service
|
|
||||||
import com.smallhacker.disbrowser.asm.SnesAddress
|
import com.smallhacker.disbrowser.asm.SnesAddress
|
||||||
import com.smallhacker.disbrowser.asm.VagueNumber
|
import com.smallhacker.disbrowser.asm.VagueNumber
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
@ -17,7 +16,21 @@ class DisassemblyResource {
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.TEXT_HTML)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
fun getIt() = handle {
|
fun getIt() = handle {
|
||||||
Service.showDisassemblyFromReset()
|
//Service.showDisassemblyFromReset()
|
||||||
|
table {
|
||||||
|
Service.getVectors().forEach {
|
||||||
|
tr {
|
||||||
|
td { text(it.name) }
|
||||||
|
td { text("(" + it.vectorLocation.toFormattedString() + ")") }
|
||||||
|
td {
|
||||||
|
a {
|
||||||
|
text(it.label)
|
||||||
|
}.attr("href", "/${it.codeLocation.toSimpleString()}/mx")
|
||||||
|
}
|
||||||
|
td { text("(" + it.codeLocation.toFormattedString() + ")") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.addClass("vector-table")
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -39,14 +52,24 @@ class DisassemblyResource {
|
||||||
|
|
||||||
private fun handle(runner: () -> HtmlNode?): Response {
|
private fun handle(runner: () -> HtmlNode?): Response {
|
||||||
try {
|
try {
|
||||||
val disassembly = runner()
|
val output = runner()
|
||||||
|
?: return Response.status(404).build()
|
||||||
|
|
||||||
return if (disassembly == null)
|
val html = html {
|
||||||
Response.status(404).build()
|
head {
|
||||||
else
|
title { text("Disassembly Browser") }
|
||||||
Response.ok(disassembly.toString().toByteArray(StandardCharsets.UTF_8))
|
link {}.attr("rel", "stylesheet").attr("href", "/resources/style.css")
|
||||||
.encoding("UTF-8")
|
meta {}.attr("charset", "UTF-8")
|
||||||
.build()
|
}
|
||||||
|
body {
|
||||||
|
output.appendTo(parent)
|
||||||
|
script().attr("src", "/resources/disbrowser.js")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.ok(html.toString().toByteArray(StandardCharsets.UTF_8))
|
||||||
|
.encoding("UTF-8")
|
||||||
|
.build()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
throw e
|
throw e
|
||||||
|
|
|
@ -25,6 +25,10 @@ fun toHex(value: UInt, bytes: UInt): String {
|
||||||
return String.format(pattern, value.toInt())
|
return String.format(pattern, value.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toHex(value: UInt24) = toHex(value.toUInt(), 3u)
|
||||||
|
fun toHex(value: UShort) = toHex(value.toUInt(), 2u)
|
||||||
|
fun toHex(value: UByte) = toHex(value.toUInt(), 1u)
|
||||||
|
|
||||||
fun joinBytes(vararg bytes: UByte) = bytes
|
fun joinBytes(vararg bytes: UByte) = bytes
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.mapIndexed { index, v -> v.toUInt() shl (index * 8) }
|
.mapIndexed { index, v -> v.toUInt() shl (index * 8) }
|
||||||
|
@ -74,4 +78,12 @@ fun Short.toUInt24() = this.toUInt().toUInt24()
|
||||||
fun Byte.toUInt24() = this.toUInt().toUInt24()
|
fun Byte.toUInt24() = this.toUInt().toUInt24()
|
||||||
|
|
||||||
fun <T> List<T>.asReverseSequence(): Sequence<T> =
|
fun <T> List<T>.asReverseSequence(): Sequence<T> =
|
||||||
((size - 1) downTo 0).asSequence().map { this[it] }
|
((size - 1) downTo 0).asSequence().map { this[it] }
|
||||||
|
|
||||||
|
fun <K, V> MutableMap<K, V>.removeIf(condition: (K, V) -> Boolean) {
|
||||||
|
this.asSequence()
|
||||||
|
.filter { condition(it.key, it.value) }
|
||||||
|
.map { it.key }
|
||||||
|
.toList()
|
||||||
|
.forEach { remove(it) }
|
||||||
|
}
|
|
@ -107,4 +107,24 @@ tr.line-active {
|
||||||
|
|
||||||
.opcode-info{
|
.opcode-info{
|
||||||
text-decoration: green underline dotted;
|
text-decoration: green underline dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-editable-popup-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 5px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.field-editable-popup-icon:hover,
|
||||||
|
.field-editable-popup:hover > .field-editable-popup-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-editable-popup-icon:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.field-editable-popup-icon::before {
|
||||||
|
content: "[e]"
|
||||||
}
|
}
|
|
@ -67,10 +67,10 @@ window.addEventListener("hashchange", function () {
|
||||||
highlight(fromHash())
|
highlight(fromHash())
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
let comments = document.getElementsByClassName("field-editable");
|
let editables = document.getElementsByClassName("field-editable");
|
||||||
for (let i = 0; i < comments.length; i++) {
|
for (let i = 0; i < editables.length; i++) {
|
||||||
let comment = comments[i];
|
let editable = editables[i];
|
||||||
comment.addEventListener("change", e => {
|
editable.addEventListener("change", e => {
|
||||||
let target = <HTMLInputElement>(e.target);
|
let target = <HTMLInputElement>(e.target);
|
||||||
let field = target.dataset.field || "";
|
let field = target.dataset.field || "";
|
||||||
let address = target.dataset.address;
|
let address = target.dataset.address;
|
||||||
|
@ -79,6 +79,30 @@ for (let i = 0; i < comments.length; i++) {
|
||||||
xhr(`/rest/${address}/${field}`, "POST", value)
|
xhr(`/rest/${address}/${field}`, "POST", value)
|
||||||
.catch((xhr: XMLHttpRequest) => alert("Error: HTTP " + xhr.status));
|
.catch((xhr: XMLHttpRequest) => alert("Error: HTTP " + xhr.status));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let popupEditables = document.getElementsByClassName("field-editable-popup");
|
||||||
|
for (let i = 0; i < popupEditables.length; i++) {
|
||||||
|
let editable = <HTMLSpanElement>(popupEditables[i]);
|
||||||
|
let first = editable.getElementsByClassName("field-editable-popup-icon")[0];
|
||||||
|
if (!first) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
first.addEventListener("click", e => {
|
||||||
|
let field = editable.dataset.field || "";
|
||||||
|
let address = editable.dataset.address;
|
||||||
|
let value = editable.dataset.value;
|
||||||
|
let newValue = prompt("Label for $" + address, value);
|
||||||
|
if (newValue === null || newValue == value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr(`/rest/${address}/${field}`, "POST", newValue)
|
||||||
|
.then(() => location.reload())
|
||||||
|
.catch((xhr: XMLHttpRequest) => alert("Error: HTTP " + xhr.status));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
Loading…
Reference in New Issue