mirror of
https://github.com/Smallhacker/disbrowser.git
synced 2024-06-07 14:35:30 +00:00
Stuff
This commit is contained in:
parent
32f3cdabff
commit
9881bf9e0b
|
@ -1,39 +1,155 @@
|
||||||
{
|
{
|
||||||
"008000" : {
|
"code" : {
|
||||||
"label" : "ResetVector"
|
"008000" : {
|
||||||
},
|
"label" : "ResetVector"
|
||||||
"008034" : {
|
},
|
||||||
"label" : "MainGameLoop"
|
"00801b" : {
|
||||||
},
|
"comment" : "\\ Turn off emulation mode"
|
||||||
"0080b5" : {
|
},
|
||||||
"label" : "JumpToGameMode"
|
"00801c" : {
|
||||||
},
|
"comment" : "/"
|
||||||
"0080c6" : {
|
},
|
||||||
"flags" : [ {
|
"00801f" : {
|
||||||
"flagType" : "JmpIndirectLongInterleavedTable",
|
"comment" : "\\ Set direct page"
|
||||||
"start" : "008061",
|
},
|
||||||
"entries" : 28
|
"008022" : {
|
||||||
} ]
|
"comment" : "/"
|
||||||
},
|
},
|
||||||
"00841e" : {
|
"008023" : {
|
||||||
"label" : "ClearOam",
|
"comment" : "\\ Set stack position"
|
||||||
"comment" : "Test3"
|
},
|
||||||
},
|
"008026" : {
|
||||||
"00879c" : {
|
"comment" : "/"
|
||||||
"flags" : [ {
|
},
|
||||||
"flagType" : "NonReturningRoutine"
|
"008034" : {
|
||||||
} ]
|
"label" : "MainGameLoop",
|
||||||
},
|
"comment" : "Wait for NMI"
|
||||||
"0287d0" : {
|
},
|
||||||
"flags" : [ {
|
"00805d" : {
|
||||||
"flagType" : "JslTableRoutine",
|
"comment" : "Clear NMI flag"
|
||||||
"entries" : 4
|
},
|
||||||
} ]
|
"0080b5" : {
|
||||||
},
|
"label" : "JumpToGameMode",
|
||||||
"0cc115" : {
|
"comment" : "Y = Current game mode"
|
||||||
"flags" : [ {
|
},
|
||||||
"flagType" : "JslTableRoutine",
|
"0080b7" : {
|
||||||
"entries" : 12
|
"comment" : "\\ Load routine low byte"
|
||||||
} ]
|
},
|
||||||
|
"0080ba" : {
|
||||||
|
"comment" : "/"
|
||||||
|
},
|
||||||
|
"0080bc" : {
|
||||||
|
"comment" : "\\ Load routine mid byte"
|
||||||
|
},
|
||||||
|
"0080bf" : {
|
||||||
|
"comment" : "/"
|
||||||
|
},
|
||||||
|
"0080c1" : {
|
||||||
|
"comment" : "\\ Load routine high byte"
|
||||||
|
},
|
||||||
|
"0080c4" : {
|
||||||
|
"comment" : "/"
|
||||||
|
},
|
||||||
|
"0080c6" : {
|
||||||
|
"flags" : [ {
|
||||||
|
"flagType" : "JmpIndirectLongInterleavedTable",
|
||||||
|
"start" : "008061",
|
||||||
|
"entries" : 28
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"00841e" : {
|
||||||
|
"label" : "ClearOam",
|
||||||
|
"comment" : "Test3"
|
||||||
|
},
|
||||||
|
"00879c" : {
|
||||||
|
"comment" : "Preserve Y value for later",
|
||||||
|
"flags" : [ {
|
||||||
|
"flagType" : "NonReturningRoutine"
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"00879e" : {
|
||||||
|
"comment" : "Y = Ret.Bank"
|
||||||
|
},
|
||||||
|
"00879f" : {
|
||||||
|
"comment" : "$02 = Ret.Bank"
|
||||||
|
},
|
||||||
|
"0087a3" : {
|
||||||
|
"comment" : "\\"
|
||||||
|
},
|
||||||
|
"0087a6" : {
|
||||||
|
"comment" : "|"
|
||||||
|
},
|
||||||
|
"0087a8" : {
|
||||||
|
"comment" : "| Y = In.A * 3"
|
||||||
|
},
|
||||||
|
"0087a9" : {
|
||||||
|
"comment" : "|"
|
||||||
|
},
|
||||||
|
"0087ab" : {
|
||||||
|
"comment" : "/"
|
||||||
|
},
|
||||||
|
"0087ac" : {
|
||||||
|
"comment" : "\\ $03-$04 = Ret.Offset"
|
||||||
|
},
|
||||||
|
"0087ad" : {
|
||||||
|
"comment" : "/"
|
||||||
|
},
|
||||||
|
"0087af" : {
|
||||||
|
"comment" : "Increase Y to compensate for Ret being off by one"
|
||||||
|
},
|
||||||
|
"0087b0" : {
|
||||||
|
"comment" : "\\"
|
||||||
|
},
|
||||||
|
"0087b2" : {
|
||||||
|
"comment" : "|"
|
||||||
|
},
|
||||||
|
"0087b4" : {
|
||||||
|
"comment" : "| Load target pointer into $00-03 (last byte unused)"
|
||||||
|
},
|
||||||
|
"0087b5" : {
|
||||||
|
"comment" : "|"
|
||||||
|
},
|
||||||
|
"0087b7" : {
|
||||||
|
"comment" : "/"
|
||||||
|
},
|
||||||
|
"0087bb" : {
|
||||||
|
"comment" : "Restore initial Y value"
|
||||||
|
},
|
||||||
|
"0087bd" : {
|
||||||
|
"comment" : "Jump to pointer"
|
||||||
|
},
|
||||||
|
"008901" : {
|
||||||
|
"comment" : "\\"
|
||||||
|
},
|
||||||
|
"008903" : {
|
||||||
|
"comment" : "|"
|
||||||
|
},
|
||||||
|
"008905" : {
|
||||||
|
"comment" : "| Write #$19:8000 to $00"
|
||||||
|
},
|
||||||
|
"008907" : {
|
||||||
|
"comment" : "|"
|
||||||
|
},
|
||||||
|
"008909" : {
|
||||||
|
"comment" : "|"
|
||||||
|
},
|
||||||
|
"00890b" : {
|
||||||
|
"comment" : "/"
|
||||||
|
},
|
||||||
|
"0287d0" : {
|
||||||
|
"flags" : [ {
|
||||||
|
"flagType" : "JslTableRoutine",
|
||||||
|
"entries" : 4
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"029ee3" : {
|
||||||
|
"label" : "GM_TriforceRoom"
|
||||||
|
},
|
||||||
|
"0cc115" : {
|
||||||
|
"flags" : [ {
|
||||||
|
"flagType" : "JslTableRoutine",
|
||||||
|
"entries" : 12
|
||||||
|
} ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,13 +9,13 @@ class Grid {
|
||||||
|
|
||||||
private val content = HashMap<Pair<Int, Int>, HtmlNode?>()
|
private val content = HashMap<Pair<Int, Int>, HtmlNode?>()
|
||||||
private val cellClasses = HashMap<Pair<Int, Int>, String>()
|
private val cellClasses = HashMap<Pair<Int, Int>, String>()
|
||||||
private val addresses = HashMap<Address, Int>()
|
private val addresses = HashMap<SnesAddress, Int>()
|
||||||
private val rowClasses = HashMap<Int, String>()
|
private val rowClasses = HashMap<Int, String>()
|
||||||
private val rowId = HashMap<Int, String>()
|
private val rowId = HashMap<Int, String>()
|
||||||
private var height = 0
|
private var height = 0
|
||||||
private var nextAddress: Address? = null
|
private var nextAddress: SnesAddress? = null
|
||||||
|
|
||||||
fun arrow(from: Address, to: Address) {
|
fun arrow(from: SnesAddress, to: SnesAddress) {
|
||||||
val yStart = addresses[from]
|
val yStart = addresses[from]
|
||||||
val yEnd = addresses[to]
|
val yEnd = addresses[to]
|
||||||
if (yStart == null || yEnd == null) {
|
if (yStart == null || yEnd == null) {
|
||||||
|
@ -60,7 +60,7 @@ 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 insMetadata = ins.address?.let { metadata[it] }
|
||||||
|
|
||||||
val actualAddress = ins.address
|
val actualAddress = ins.address
|
||||||
val presentedAddress = ins.presentedAddress
|
val presentedAddress = ins.presentedAddress
|
||||||
|
@ -80,7 +80,7 @@ class Grid {
|
||||||
text(ins.bytesToString()),
|
text(ins.bytesToString()),
|
||||||
editableField(presentedAddress, "label", insMetadata?.label),
|
editableField(presentedAddress, "label", insMetadata?.label),
|
||||||
fragment {
|
fragment {
|
||||||
text(ins.printOpcodeAndSuffix())
|
opcodeNode(ins)
|
||||||
text(" ")
|
text(" ")
|
||||||
var operands = ins.printOperands()
|
var operands = ins.printOperands()
|
||||||
|
|
||||||
|
@ -111,7 +111,19 @@ class Grid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun editableField(address: Address, type: String, value: String?): HtmlNode {
|
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 {
|
||||||
return input(value = value ?: "")
|
return input(value = value ?: "")
|
||||||
.addClass("field-$type")
|
.addClass("field-$type")
|
||||||
.addClass("field-editable")
|
.addClass("field-editable")
|
||||||
|
@ -124,7 +136,7 @@ class Grid {
|
||||||
add(y, null, null, null, null, text("..."), null, null)
|
add(y, null, null, null, null, text("..."), null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun add(y: Int, address: Address?,
|
private fun add(y: Int, address: SnesAddress?,
|
||||||
cAddress: HtmlNode?,
|
cAddress: HtmlNode?,
|
||||||
cBytes: HtmlNode?,
|
cBytes: HtmlNode?,
|
||||||
cLabel: HtmlNode?,
|
cLabel: HtmlNode?,
|
||||||
|
|
|
@ -99,6 +99,8 @@ fun body(inner: InnerHtml = {}) = parent("body", inner)
|
||||||
fun HtmlArea.body(inner: InnerHtml = {}) = com.smallhacker.disbrowser.body(inner).appendTo(parent)
|
fun HtmlArea.body(inner: InnerHtml = {}) = com.smallhacker.disbrowser.body(inner).appendTo(parent)
|
||||||
fun div(inner: InnerHtml = {}) = parent("div", inner)
|
fun div(inner: InnerHtml = {}) = parent("div", inner)
|
||||||
fun HtmlArea.div(inner: InnerHtml = {}) = com.smallhacker.disbrowser.div(inner).appendTo(parent)
|
fun HtmlArea.div(inner: InnerHtml = {}) = com.smallhacker.disbrowser.div(inner).appendTo(parent)
|
||||||
|
fun span(inner: InnerHtml = {}) = parent("span", inner)
|
||||||
|
fun HtmlArea.span(inner: InnerHtml = {}) = com.smallhacker.disbrowser.span(inner).appendTo(parent)
|
||||||
fun table(inner: InnerHtml = {}) = parent("table", inner)
|
fun table(inner: InnerHtml = {}) = parent("table", inner)
|
||||||
fun HtmlArea.table(inner: InnerHtml = {}) = com.smallhacker.disbrowser.table(inner).appendTo(parent)
|
fun HtmlArea.table(inner: InnerHtml = {}) = com.smallhacker.disbrowser.table(inner).appendTo(parent)
|
||||||
fun tr(inner: InnerHtml = {}) = parent("tr", inner)
|
fun tr(inner: InnerHtml = {}) = parent("tr", inner)
|
||||||
|
|
|
@ -23,12 +23,12 @@ object Service {
|
||||||
|
|
||||||
fun showDisassemblyFromReset(): HtmlNode? {
|
fun showDisassemblyFromReset(): HtmlNode? {
|
||||||
val resetVectorLocation = RESET_VECTOR_LOCATION
|
val resetVectorLocation = RESET_VECTOR_LOCATION
|
||||||
val initialAddress = Address(romData.value.getWord(resetVectorLocation.pc).toUInt24())
|
val initialAddress = SnesAddress(romData.value.getWord(resetVectorLocation.pc).toUInt24())
|
||||||
val flags = VagueNumber(0x30u)
|
val flags = VagueNumber(0x30u)
|
||||||
return showDisassembly(initialAddress, flags)
|
return showDisassembly(initialAddress, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showDisassembly(initialAddress: Address, flags: VagueNumber): HtmlNode? {
|
fun showDisassembly(initialAddress: SnesAddress, flags: VagueNumber): HtmlNode? {
|
||||||
val rom = romData.value
|
val rom = romData.value
|
||||||
|
|
||||||
val initialState = State(data = rom, address = initialAddress, flags = flags)
|
val initialState = State(data = rom, address = initialAddress, flags = flags)
|
||||||
|
@ -66,7 +66,7 @@ object Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateMetadata(address: Address, field: KMutableProperty1<MetadataLine, String?>, value: String) {
|
fun updateMetadata(address: SnesAddress, field: KMutableProperty1<MetadataLine, String?>, value: String) {
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
if (address in metadata) {
|
if (address in metadata) {
|
||||||
doUpdateMetadata(address, field, null)
|
doUpdateMetadata(address, field, null)
|
||||||
|
@ -76,7 +76,7 @@ object Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doUpdateMetadata(address: Address, field: KMutableProperty1<MetadataLine, String?>, value: String?) {
|
private fun doUpdateMetadata(address: SnesAddress, field: KMutableProperty1<MetadataLine, String?>, value: String?) {
|
||||||
val line = metadata.getOrCreate(address)
|
val line = metadata.getOrCreate(address)
|
||||||
field.set(line, value)
|
field.set(line, value)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.smallhacker.disbrowser.asm
|
||||||
class Disassembly(lines: List<CodeUnit>) : Iterable<CodeUnit> {
|
class Disassembly(lines: List<CodeUnit>) : Iterable<CodeUnit> {
|
||||||
override fun iterator() = lineList.iterator() as Iterator<CodeUnit>
|
override fun iterator() = lineList.iterator() as Iterator<CodeUnit>
|
||||||
|
|
||||||
private val knownAddresses = HashSet<Address>()
|
private val knownAddresses = HashSet<SnesAddress>()
|
||||||
private val lineList = ArrayList<CodeUnit>()
|
private val lineList = ArrayList<CodeUnit>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -16,5 +16,5 @@ class Disassembly(lines: List<CodeUnit>) : Iterable<CodeUnit> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun contains(address: Address) = address in knownAddresses
|
operator fun contains(address: SnesAddress) = address in knownAddresses
|
||||||
}
|
}
|
|
@ -3,10 +3,10 @@ package com.smallhacker.disbrowser.asm
|
||||||
import com.smallhacker.disbrowser.util.*
|
import com.smallhacker.disbrowser.util.*
|
||||||
|
|
||||||
interface CodeUnit {
|
interface CodeUnit {
|
||||||
val address: Address?
|
val address: SnesAddress?
|
||||||
val relativeAddress: Address
|
val relativeAddress: SnesAddress
|
||||||
val presentedAddress: Address
|
val presentedAddress: SnesAddress
|
||||||
val nextPresentedAddress: Address
|
val nextPresentedAddress: SnesAddress
|
||||||
|
|
||||||
val linkedState: State?
|
val linkedState: State?
|
||||||
val preState: State?
|
val preState: State?
|
||||||
|
@ -22,6 +22,11 @@ interface CodeUnit {
|
||||||
val suffix = lengthSuffix ?: ""
|
val suffix = lengthSuffix ?: ""
|
||||||
return "$mnemonic$suffix"
|
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 printOperands() = opcode.mode.print(this)
|
||||||
|
|
||||||
fun bytesToString(): String {
|
fun bytesToString(): String {
|
||||||
|
@ -58,22 +63,22 @@ interface CodeUnit {
|
||||||
class DataBlock(
|
class DataBlock(
|
||||||
override val opcode: Opcode,
|
override val opcode: Opcode,
|
||||||
override val bytes: RomData,
|
override val bytes: RomData,
|
||||||
override val presentedAddress: Address,
|
override val presentedAddress: SnesAddress,
|
||||||
override val relativeAddress: Address,
|
override val relativeAddress: SnesAddress,
|
||||||
override val linkedState: State?
|
override val linkedState: State?
|
||||||
) : CodeUnit {
|
) : CodeUnit {
|
||||||
override val nextPresentedAddress: Address
|
override val nextPresentedAddress: SnesAddress
|
||||||
get() = presentedAddress + operandLength.toInt()
|
get() = presentedAddress + operandLength.toInt()
|
||||||
override val operandLength get() = bytes.size
|
override val operandLength get() = bytes.size
|
||||||
|
|
||||||
override val address: Address? = null
|
override val address: SnesAddress? = null
|
||||||
override val preState: State? = null
|
override val preState: State? = null
|
||||||
override val postState: State? = null
|
override val postState: State? = null
|
||||||
override val lengthSuffix: String? = null
|
override val lengthSuffix: String? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
class Instruction(override val bytes: RomData, override val opcode: Opcode, override val preState: State) : CodeUnit {
|
class Instruction(override val bytes: RomData, override val opcode: Opcode, override val preState: State) : CodeUnit {
|
||||||
override val address: Address 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
|
||||||
override val nextPresentedAddress get() = postState.address
|
override val nextPresentedAddress get() = postState.address
|
||||||
|
@ -105,18 +110,18 @@ class Instruction(override val bytes: RomData, override val opcode: Opcode, over
|
||||||
override val operandLength
|
override val operandLength
|
||||||
get() = opcode.mode.operandLength(preState)
|
get() = opcode.mode.operandLength(preState)
|
||||||
|
|
||||||
private fun link(): Address? {
|
private fun link(): SnesAddress? {
|
||||||
if (!opcode.link) {
|
if (!opcode.link) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return when (opcode.mode) {
|
return when (opcode.mode) {
|
||||||
Mode.ABSOLUTE -> relativeAddress.withinBank(word)
|
Mode.ABSOLUTE -> relativeAddress.withinBank(word)
|
||||||
Mode.ABSOLUTE_LONG -> Address(long)
|
Mode.ABSOLUTE_LONG -> SnesAddress(long)
|
||||||
Mode.RELATIVE -> relativeAddress + 2 + signedByte.toInt()
|
Mode.RELATIVE -> relativeAddress + 2 + signedByte.toInt()
|
||||||
Mode.RELATIVE_LONG -> relativeAddress + 3 + signedWord.toInt()
|
Mode.RELATIVE_LONG -> relativeAddress + 3 + signedWord.toInt()
|
||||||
Mode.DATA_WORD -> relativeAddress.withinBank(word)
|
Mode.DATA_WORD -> relativeAddress.withinBank(word)
|
||||||
Mode.DATA_LONG -> Address(long)
|
Mode.DATA_LONG -> SnesAddress(long)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,36 +7,34 @@ import com.smallhacker.disbrowser.util.toUInt24
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class Metadata {
|
class Metadata {
|
||||||
private val lines: MutableMap<Address, MetadataLine>
|
@JsonProperty
|
||||||
|
private val code: TreeMap<SnesAddress, MetadataLine>
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.lines = HashMap()
|
this.code = TreeMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
private constructor(@JsonProperty lines: Map<Address, MetadataLine>) {
|
private constructor(@JsonProperty code: Map<SnesAddress, MetadataLine>) {
|
||||||
this.lines = HashMap(lines)
|
this.code = TreeMap(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonValue
|
operator fun set(address: SnesAddress, line: MetadataLine?): Metadata {
|
||||||
private fun serialize() = TreeMap(lines)
|
|
||||||
|
|
||||||
operator fun set(address: Address, line: MetadataLine?): Metadata {
|
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
lines.remove(address)
|
code.remove(address)
|
||||||
} else {
|
} else {
|
||||||
lines[address] = line
|
code[address] = line
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(address: Address): MetadataLine? {
|
operator fun get(address: SnesAddress): MetadataLine? {
|
||||||
return lines[address]
|
return code[address]
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun contains(address: Address) = lines[address] != null
|
operator fun contains(address: SnesAddress) = code[address] != null
|
||||||
|
|
||||||
fun getOrCreate(address: Address): MetadataLine {
|
fun getOrCreate(address: SnesAddress): MetadataLine {
|
||||||
val line = this[address]
|
val line = this[address]
|
||||||
if (line != null) {
|
if (line != null) {
|
||||||
return line
|
return line
|
||||||
|
@ -60,17 +58,17 @@ object NonReturningRoutine : InstructionFlag {
|
||||||
}
|
}
|
||||||
|
|
||||||
class JmpIndirectLongInterleavedTable @JsonCreator constructor(
|
class JmpIndirectLongInterleavedTable @JsonCreator constructor(
|
||||||
@field:JsonProperty @JsonProperty private val start: Address,
|
@field:JsonProperty @JsonProperty private val start: SnesAddress,
|
||||||
@field:JsonProperty @JsonProperty private val entries: Int
|
@field:JsonProperty @JsonProperty private val entries: Int
|
||||||
) : InstructionFlag {
|
) : InstructionFlag {
|
||||||
private val uEntries get() = entries.toUInt()
|
private val uEntries get() = entries.toUInt()
|
||||||
|
|
||||||
fun readTable(data: RomData): Sequence<Address> {
|
fun readTable(data: RomData): Sequence<SnesAddress> {
|
||||||
return (0u until uEntries)
|
return (0u until uEntries)
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.map { it + start.pc }
|
.map { it + start.pc }
|
||||||
.map { pc -> joinBytes(data[pc], data[pc + uEntries], data[pc + uEntries + uEntries]).toUInt24() }
|
.map { pc -> joinBytes(data[pc], data[pc + uEntries], data[pc + uEntries + uEntries]).toUInt24() }
|
||||||
.map { Address(it) }
|
.map { SnesAddress(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateCode(jumpInstruction: Instruction): Sequence<DataBlock> {
|
fun generateCode(jumpInstruction: Instruction): Sequence<DataBlock> {
|
||||||
|
@ -92,7 +90,7 @@ class JmpIndirectLongInterleavedTable @JsonCreator constructor(
|
||||||
jumpInstruction.postState.address + offset.toInt(),
|
jumpInstruction.postState.address + offset.toInt(),
|
||||||
jumpInstruction.relativeAddress,
|
jumpInstruction.relativeAddress,
|
||||||
jumpInstruction.opcode.mutate(jumpInstruction)
|
jumpInstruction.opcode.mutate(jumpInstruction)
|
||||||
.mutateAddress { Address(target) }
|
.mutateAddress { SnesAddress(target) }
|
||||||
.withOrigin(jumpInstruction)
|
.withOrigin(jumpInstruction)
|
||||||
)
|
)
|
||||||
// Instruction(
|
// Instruction(
|
||||||
|
@ -110,13 +108,13 @@ class JslTableRoutine @JsonCreator constructor(
|
||||||
@field:JsonProperty @JsonProperty private val entries: Int
|
@field:JsonProperty @JsonProperty private val entries: Int
|
||||||
) : InstructionFlag {
|
) : InstructionFlag {
|
||||||
|
|
||||||
fun readTable(postJsr: State): Sequence<Address> {
|
fun readTable(postJsr: State): Sequence<SnesAddress> {
|
||||||
val data = postJsr.data
|
val data = postJsr.data
|
||||||
return (0u until entries.toUInt())
|
return (0u until entries.toUInt())
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.map { postJsr.address.pc + (it * 3u) }
|
.map { postJsr.address.pc + (it * 3u) }
|
||||||
.map { pc -> joinBytes(data[pc], data[pc + 1u], data[pc + 2u]).toUInt24() }
|
.map { pc -> joinBytes(data[pc], data[pc + 1u], data[pc + 2u]).toUInt24() }
|
||||||
.map { Address(it) }
|
.map { SnesAddress(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = "JslTableRoutine($entries)"
|
override fun toString() = "JslTableRoutine($entries)"
|
||||||
|
|
|
@ -8,8 +8,9 @@ data class MetadataLine(
|
||||||
var label: String? = null,
|
var label: String? = null,
|
||||||
var comment: String? = null,
|
var comment: String? = null,
|
||||||
var preComment: String? = null,
|
var preComment: String? = null,
|
||||||
|
var length: Int? = null,
|
||||||
val flags: MutableList<InstructionFlag> = ArrayList()
|
val flags: MutableList<InstructionFlag> = ArrayList()
|
||||||
) {
|
) {
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun isEmpty() = (label == null) && (comment == null) && (preComment == null) && (flags.isEmpty())
|
fun isEmpty() = (label == null) && (comment == null) && (preComment == null) && (length == 0) && (flags.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
13
src/main/java/com/smallhacker/disbrowser/asm/MetadataRam.kt
Normal file
13
src/main/java/com/smallhacker/disbrowser/asm/MetadataRam.kt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||||
|
data class MetadataRam(
|
||||||
|
var label: String? = null,
|
||||||
|
var length: Int?
|
||||||
|
) {
|
||||||
|
@JsonIgnore
|
||||||
|
fun isEmpty() = (label == null) && (length == null)
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
enum class Mnemonic(private val nameOverride: String? = null) {
|
enum class Mnemonic(private val nameOverride: String? = null, val alternativeName: String? = null) {
|
||||||
ADC, AND, ASL, BCC, BCS, 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, JML, JSL,
|
||||||
JSR, LDA, LDX, LDY, LSR, MVN, MVP, NOP, ORA, PEA, PEI,
|
JSR, LDA, LDX, LDY, LSR, MVN, MVP, NOP, ORA, PEA, PEI,
|
||||||
|
@ -11,7 +11,7 @@ enum class Mnemonic(private val nameOverride: String? = null) {
|
||||||
TCS, TDC, TRB, TSB, TSC, TSX, TXA, TXS, TXY, TYA, TYX,
|
TCS, TDC, TRB, TSB, TSC, TSX, TXA, TXS, TXY, TYA, TYX,
|
||||||
WAI, WDM, XBA, XCE,
|
WAI, WDM, XBA, XCE,
|
||||||
|
|
||||||
DB(".db"), DW(".dw"), DL(".dl");
|
DB(nameOverride = ".db"), DW(nameOverride = ".dw"), DL(nameOverride = ".dl");
|
||||||
|
|
||||||
val displayName get() = nameOverride ?: name
|
val displayName get() = nameOverride ?: name
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
class Segment (val start: Address, val end: SegmentEnd, val instructions: List<Instruction>)
|
class Segment (val start: SnesAddress, val end: SegmentEnd, val instructions: List<Instruction>)
|
||||||
|
|
||||||
class SegmentEnd(val address: Address, val local: List<State> = emptyList(), val remote: List<State> = emptyList(), val returnAddress: Address? = null, val returning: Boolean = false)
|
class SegmentEnd(val address: SnesAddress, val local: List<State> = emptyList(), val remote: List<State> = emptyList(), val returnAddress: SnesAddress? = null, val returning: Boolean = false)
|
||||||
|
|
||||||
fun stoppingSegmentEnd(address: Address)
|
fun stoppingSegmentEnd(address: SnesAddress)
|
||||||
= SegmentEnd(address)
|
= SegmentEnd(address)
|
||||||
|
|
||||||
fun branchingSegmentEnd(address: Address, continueState: State, branchState: State)
|
fun branchingSegmentEnd(address: SnesAddress, continueState: State, branchState: State)
|
||||||
= SegmentEnd(address, local = listOf(continueState, branchState))
|
= SegmentEnd(address, local = listOf(continueState, branchState))
|
||||||
|
|
||||||
fun alwaysBranchingSegmentEnd(address: Address, branchState: State)
|
fun alwaysBranchingSegmentEnd(address: SnesAddress, branchState: State)
|
||||||
= SegmentEnd(address, local = listOf(branchState))
|
= SegmentEnd(address, local = listOf(branchState))
|
||||||
|
|
||||||
fun jumpSegmentEnd(address: Address, targetState: State)
|
fun jumpSegmentEnd(address: SnesAddress, targetState: State)
|
||||||
= SegmentEnd(address, remote = listOf(targetState))
|
= SegmentEnd(address, remote = listOf(targetState))
|
||||||
|
|
||||||
fun subroutineSegmentEnd(address: Address, targetState: State, returnAddress: Address)
|
fun subroutineSegmentEnd(address: SnesAddress, targetState: State, returnAddress: SnesAddress)
|
||||||
= SegmentEnd(address, remote = listOf(targetState), returnAddress = returnAddress)
|
= SegmentEnd(address, remote = listOf(targetState), returnAddress = returnAddress)
|
||||||
|
|
||||||
fun returnSegmentEnd(address: Address)
|
fun returnSegmentEnd(address: SnesAddress)
|
||||||
= SegmentEnd(address, returning = true)
|
= SegmentEnd(address, returning = true)
|
||||||
|
|
||||||
fun continuationSegmentEnd(state: State)
|
fun continuationSegmentEnd(state: State)
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator
|
import com.fasterxml.jackson.annotation.JsonCreator
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.annotation.JsonValue
|
import com.fasterxml.jackson.annotation.JsonValue
|
||||||
import com.smallhacker.disbrowser.util.UInt24
|
import com.smallhacker.disbrowser.util.UInt24
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.disbrowser.util.toUInt24
|
||||||
import com.smallhacker.disbrowser.util.tryParseInt
|
import com.smallhacker.disbrowser.util.tryParseInt
|
||||||
|
|
||||||
data class Address(val value: UInt24) : Comparable<Address> {
|
data class SnesAddress(val value: UInt24) : Comparable<SnesAddress> {
|
||||||
val rom = (value and 0x8000u).toUInt() == 0u
|
|
||||||
val pc = snesToPc(value)
|
val pc = snesToPc(value)
|
||||||
|
|
||||||
operator fun plus(offset: Int) = Address(value + offset)
|
operator fun plus(offset: Int) = SnesAddress(value + offset)
|
||||||
operator fun minus(offset: Int) = Address(value - offset)
|
operator fun minus(offset: Int) = SnesAddress(value - offset)
|
||||||
operator fun inc() = plus(1)
|
operator fun inc() = plus(1)
|
||||||
operator fun dec() = minus(1)
|
operator fun dec() = minus(1)
|
||||||
|
|
||||||
|
@ -22,21 +19,21 @@ data class Address(val value: UInt24) : Comparable<Address> {
|
||||||
@JsonValue
|
@JsonValue
|
||||||
fun toSimpleString(): String = String.format("%06x", value.toInt())
|
fun toSimpleString(): String = String.format("%06x", value.toInt())
|
||||||
|
|
||||||
fun withinBank(value: UShort): Address = Address((this.value and 0xFF_0000u) or value.toUInt24())
|
fun withinBank(value: UShort): SnesAddress = SnesAddress((this.value and 0xFF_0000u) or value.toUInt24())
|
||||||
|
|
||||||
override fun compareTo(other: Address) = value.toUInt().compareTo(other.value.toUInt())
|
override fun compareTo(other: SnesAddress) = value.toUInt().compareTo(other.value.toUInt())
|
||||||
|
|
||||||
infix fun distanceTo(other: Address) = Math.abs(value.toInt() - other.value.toInt()).toUInt()
|
infix fun distanceTo(other: SnesAddress) = Math.abs(value.toInt() - other.value.toInt()).toUInt()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
fun parse(address: String): Address? = tryParseInt(address, 16)
|
fun parse(address: String): SnesAddress? = tryParseInt(address, 16)
|
||||||
?.let { Address(it.toUInt24()) }
|
?.let { SnesAddress(it.toUInt24()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun address(snesAddress: Int) = Address(snesAddress.toUInt24())
|
fun address(snesAddress: Int) = SnesAddress(snesAddress.toUInt24())
|
||||||
|
|
||||||
private fun snesToPc(value: UInt24): UInt {
|
private fun snesToPc(value: UInt24): UInt {
|
||||||
// TODO: This is incredibly oversimplified. Anything that isn't a small LoROM will crash and burn
|
// TODO: This is incredibly oversimplified. Anything that isn't a small LoROM will crash and burn
|
|
@ -2,10 +2,14 @@ package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.ImmStack
|
import com.smallhacker.disbrowser.ImmStack
|
||||||
import com.smallhacker.disbrowser.immStack
|
import com.smallhacker.disbrowser.immStack
|
||||||
|
import com.smallhacker.disbrowser.util.toUInt24
|
||||||
|
|
||||||
data class State(val origin: Instruction? = null, val data: RomData, val address: Address, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack()) {
|
data class State(val origin: Instruction? = null, val data: RomData, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack()) {
|
||||||
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 dp: UShort? get() = 0x7e00u // TODO
|
||||||
|
val pb: UByte get() = (address.value shr 16).toUByte()
|
||||||
|
|
||||||
val mWidth: UInt? get() = toWidth(m)
|
val mWidth: UInt? get() = toWidth(m)
|
||||||
val xWidth: UInt? get() = toWidth(x)
|
val xWidth: UInt? get() = toWidth(x)
|
||||||
|
@ -16,7 +20,7 @@ data class State(val origin: Instruction? = null, val data: RomData, val address
|
||||||
|
|
||||||
private fun withFlags(flags: VagueNumber) = copy(flags = flags)
|
private fun withFlags(flags: VagueNumber) = copy(flags = flags)
|
||||||
|
|
||||||
fun mutateAddress(mutator: (Address) -> Address) = copy(address = mutator(address))
|
fun mutateAddress(mutator: (SnesAddress) -> SnesAddress) = copy(address = mutator(address))
|
||||||
fun withOrigin(instruction: Instruction?) = copy(origin = instruction)
|
fun withOrigin(instruction: Instruction?) = copy(origin = instruction)
|
||||||
|
|
||||||
fun push(value: UInt) = push(VagueNumber(value))
|
fun push(value: UInt) = push(VagueNumber(value))
|
||||||
|
@ -52,6 +56,16 @@ data class State(val origin: Instruction? = null, val data: RomData, val address
|
||||||
return "A:${printSize(m)} XY:${printSize(x)} S:" + stackToString()
|
return "A:${printSize(m)} XY:${printSize(x)} S:" + stackToString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resolve(directPage: UByte) = dp?.let { dp ->
|
||||||
|
val ptr = (dp.toUInt24() shl 8) or (directPage.toUInt24())
|
||||||
|
SnesAddress(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolve(absolute: UShort)= db?.let { db ->
|
||||||
|
val ptr = (db.toUInt24() shl 16) or (absolute.toUInt24())
|
||||||
|
SnesAddress(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
private fun stackToString(): String {
|
private fun stackToString(): String {
|
||||||
return stack.reversed().asSequence()
|
return stack.reversed().asSequence()
|
||||||
.map { stackByteToString(it) }
|
.map { stackByteToString(it) }
|
||||||
|
|
|
@ -6,7 +6,7 @@ import kotlin.collections.ArrayList
|
||||||
|
|
||||||
object Disassembler {
|
object Disassembler {
|
||||||
fun disassemble(initialState: State, metadata: Metadata, global: Boolean): Disassembly {
|
fun disassemble(initialState: State, metadata: Metadata, global: Boolean): Disassembly {
|
||||||
val seen = HashSet<Address>()
|
val seen = HashSet<SnesAddress>()
|
||||||
val queue = ArrayDeque<State>()
|
val queue = ArrayDeque<State>()
|
||||||
|
|
||||||
fun tryAdd(state: State) {
|
fun tryAdd(state: State) {
|
||||||
|
@ -79,7 +79,7 @@ object Disassembler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disassembleSegments(initialState: State): List<Segment> {
|
fun disassembleSegments(initialState: State): List<Segment> {
|
||||||
val mapping = HashMap<Address, Segment>()
|
val mapping = HashMap<SnesAddress, Segment>()
|
||||||
val queue = ArrayDeque<State>()
|
val queue = ArrayDeque<State>()
|
||||||
|
|
||||||
val segments = ArrayList<Segment>()
|
val segments = ArrayList<Segment>()
|
||||||
|
@ -119,12 +119,12 @@ object Disassembler {
|
||||||
return segments
|
return segments
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disassembleSegment(initialState: State, mapping: MutableMap<Address, Segment>): Segment {
|
fun disassembleSegment(initialState: State, mapping: MutableMap<SnesAddress, Segment>): Segment {
|
||||||
val instructions = ArrayList<Instruction>()
|
val instructions = ArrayList<Instruction>()
|
||||||
var lastState = initialState
|
var lastState = initialState
|
||||||
|
|
||||||
val queue = ArrayDeque<State>()
|
val queue = ArrayDeque<State>()
|
||||||
val seen = HashSet<Address>()
|
val seen = HashSet<SnesAddress>()
|
||||||
|
|
||||||
fun finalize(segment: Segment): Segment {
|
fun finalize(segment: Segment): Segment {
|
||||||
instructions.forEach {
|
instructions.forEach {
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.smallhacker.disbrowser.resource
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.HtmlNode
|
import com.smallhacker.disbrowser.HtmlNode
|
||||||
import com.smallhacker.disbrowser.Service
|
import com.smallhacker.disbrowser.Service
|
||||||
import com.smallhacker.disbrowser.asm.Address
|
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
|
||||||
import javax.ws.rs.GET
|
import javax.ws.rs.GET
|
||||||
|
@ -30,7 +30,7 @@ class DisassemblyResource {
|
||||||
@Produces(MediaType.TEXT_HTML)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
fun getIt(@PathParam("address") address: String, @PathParam("state") state: String): Response {
|
fun getIt(@PathParam("address") address: String, @PathParam("state") state: String): Response {
|
||||||
return handle {
|
return handle {
|
||||||
Address.parse(address)?.let {
|
SnesAddress.parse(address)?.let {
|
||||||
val flags = parseState(state)
|
val flags = parseState(state)
|
||||||
Service.showDisassembly(it, flags)
|
Service.showDisassembly(it, flags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.smallhacker.disbrowser.resource
|
package com.smallhacker.disbrowser.resource
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.Service
|
import com.smallhacker.disbrowser.Service
|
||||||
import com.smallhacker.disbrowser.asm.Address
|
import com.smallhacker.disbrowser.asm.SnesAddress
|
||||||
import com.smallhacker.disbrowser.asm.MetadataLine
|
import com.smallhacker.disbrowser.asm.MetadataLine
|
||||||
import javax.ws.rs.Consumes
|
import javax.ws.rs.Consumes
|
||||||
import javax.ws.rs.POST
|
import javax.ws.rs.POST
|
||||||
|
@ -16,7 +16,7 @@ class RestResource {
|
||||||
@Path("{address}/{field}")
|
@Path("{address}/{field}")
|
||||||
@Consumes(MediaType.TEXT_PLAIN)
|
@Consumes(MediaType.TEXT_PLAIN)
|
||||||
fun getIt(@PathParam("address") address: String, @PathParam("field") fieldName: String, body: String): Response {
|
fun getIt(@PathParam("address") address: String, @PathParam("field") fieldName: String, body: String): Response {
|
||||||
val parsedAddress = Address.parse(address) ?: return Response.status(400).build()
|
val parsedAddress = SnesAddress.parse(address) ?: return Response.status(400).build()
|
||||||
val field = when (fieldName) {
|
val field = when (fieldName) {
|
||||||
"preComment" -> MetadataLine::preComment
|
"preComment" -> MetadataLine::preComment
|
||||||
"comment" -> MetadataLine::comment
|
"comment" -> MetadataLine::comment
|
||||||
|
|
|
@ -99,9 +99,12 @@ tr.line-active {
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-comment {
|
.field-comment {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opcode-info{
|
||||||
|
text-decoration: green underline dotted;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user