From 9881bf9e0ba02c84a318520571fe7b60be117dc3 Mon Sep 17 00:00:00 2001 From: Smallhacker Date: Fri, 11 Jan 2019 11:35:35 -0500 Subject: [PATCH] Stuff --- ...nsetsu - Kamigami no Triforce (Japan).json | 188 ++++++++++++++---- .../java/com/smallhacker/disbrowser/Grid.kt | 26 ++- .../java/com/smallhacker/disbrowser/Html.kt | 2 + .../com/smallhacker/disbrowser/Service.kt | 8 +- .../smallhacker/disbrowser/asm/Disassembly.kt | 4 +- .../smallhacker/disbrowser/asm/Instruction.kt | 29 +-- .../smallhacker/disbrowser/asm/Metadata.kt | 38 ++-- .../disbrowser/asm/MetadataLine.kt | 3 +- .../smallhacker/disbrowser/asm/MetadataRam.kt | 13 ++ .../smallhacker/disbrowser/asm/Mnemonic.kt | 6 +- .../com/smallhacker/disbrowser/asm/Segment.kt | 16 +- .../asm/{Address.kt => SnesAddress.kt} | 21 +- .../com/smallhacker/disbrowser/asm/State.kt | 18 +- .../disbrowser/disassembler/Disassembler.kt | 8 +- .../resource/DisassemblyResource.kt | 4 +- .../disbrowser/resource/RestResource.kt | 4 +- src/main/resources/public/style.css | 5 +- 17 files changed, 277 insertions(+), 116 deletions(-) create mode 100644 src/main/java/com/smallhacker/disbrowser/asm/MetadataRam.kt rename src/main/java/com/smallhacker/disbrowser/asm/{Address.kt => SnesAddress.kt} (54%) diff --git a/Zelda no Densetsu - Kamigami no Triforce (Japan).json b/Zelda no Densetsu - Kamigami no Triforce (Japan).json index cace8fe..21e1e9e 100644 --- a/Zelda no Densetsu - Kamigami no Triforce (Japan).json +++ b/Zelda no Densetsu - Kamigami no Triforce (Japan).json @@ -1,39 +1,155 @@ { - "008000" : { - "label" : "ResetVector" - }, - "008034" : { - "label" : "MainGameLoop" - }, - "0080b5" : { - "label" : "JumpToGameMode" - }, - "0080c6" : { - "flags" : [ { - "flagType" : "JmpIndirectLongInterleavedTable", - "start" : "008061", - "entries" : 28 - } ] - }, - "00841e" : { - "label" : "ClearOam", - "comment" : "Test3" - }, - "00879c" : { - "flags" : [ { - "flagType" : "NonReturningRoutine" - } ] - }, - "0287d0" : { - "flags" : [ { - "flagType" : "JslTableRoutine", - "entries" : 4 - } ] - }, - "0cc115" : { - "flags" : [ { - "flagType" : "JslTableRoutine", - "entries" : 12 - } ] + "code" : { + "008000" : { + "label" : "ResetVector" + }, + "00801b" : { + "comment" : "\\ Turn off emulation mode" + }, + "00801c" : { + "comment" : "/" + }, + "00801f" : { + "comment" : "\\ Set direct page" + }, + "008022" : { + "comment" : "/" + }, + "008023" : { + "comment" : "\\ Set stack position" + }, + "008026" : { + "comment" : "/" + }, + "008034" : { + "label" : "MainGameLoop", + "comment" : "Wait for NMI" + }, + "00805d" : { + "comment" : "Clear NMI flag" + }, + "0080b5" : { + "label" : "JumpToGameMode", + "comment" : "Y = Current game mode" + }, + "0080b7" : { + "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 + } ] + } } } \ No newline at end of file diff --git a/src/main/java/com/smallhacker/disbrowser/Grid.kt b/src/main/java/com/smallhacker/disbrowser/Grid.kt index 77fd4ac..c689cba 100644 --- a/src/main/java/com/smallhacker/disbrowser/Grid.kt +++ b/src/main/java/com/smallhacker/disbrowser/Grid.kt @@ -9,13 +9,13 @@ class Grid { private val content = HashMap, HtmlNode?>() private val cellClasses = HashMap, String>() - private val addresses = HashMap() + private val addresses = HashMap() private val rowClasses = HashMap() private val rowId = HashMap() 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 yEnd = addresses[to] if (yStart == null || yEnd == null) { @@ -60,7 +60,7 @@ class Grid { } 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 presentedAddress = ins.presentedAddress @@ -80,7 +80,7 @@ class Grid { text(ins.bytesToString()), editableField(presentedAddress, "label", insMetadata?.label), fragment { - text(ins.printOpcodeAndSuffix()) + opcodeNode(ins) text(" ") 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 ?: "") .addClass("field-$type") .addClass("field-editable") @@ -124,7 +136,7 @@ class Grid { 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?, cBytes: HtmlNode?, cLabel: HtmlNode?, diff --git a/src/main/java/com/smallhacker/disbrowser/Html.kt b/src/main/java/com/smallhacker/disbrowser/Html.kt index f42bd06..cf6fee0 100644 --- a/src/main/java/com/smallhacker/disbrowser/Html.kt +++ b/src/main/java/com/smallhacker/disbrowser/Html.kt @@ -99,6 +99,8 @@ fun body(inner: InnerHtml = {}) = parent("body", inner) fun HtmlArea.body(inner: InnerHtml = {}) = com.smallhacker.disbrowser.body(inner).appendTo(parent) fun div(inner: InnerHtml = {}) = parent("div", inner) fun HtmlArea.div(inner: InnerHtml = {}) = com.smallhacker.disbrowser.div(inner).appendTo(parent) +fun 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 HtmlArea.table(inner: InnerHtml = {}) = com.smallhacker.disbrowser.table(inner).appendTo(parent) fun tr(inner: InnerHtml = {}) = parent("tr", inner) diff --git a/src/main/java/com/smallhacker/disbrowser/Service.kt b/src/main/java/com/smallhacker/disbrowser/Service.kt index 8027900..e76c849 100644 --- a/src/main/java/com/smallhacker/disbrowser/Service.kt +++ b/src/main/java/com/smallhacker/disbrowser/Service.kt @@ -23,12 +23,12 @@ object Service { fun showDisassemblyFromReset(): HtmlNode? { 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) return showDisassembly(initialAddress, flags) } - fun showDisassembly(initialAddress: Address, flags: VagueNumber): HtmlNode? { + fun showDisassembly(initialAddress: SnesAddress, flags: VagueNumber): HtmlNode? { val rom = romData.value val initialState = State(data = rom, address = initialAddress, flags = flags) @@ -66,7 +66,7 @@ object Service { } } - fun updateMetadata(address: Address, field: KMutableProperty1, value: String) { + fun updateMetadata(address: SnesAddress, field: KMutableProperty1, value: String) { if (value.isEmpty()) { if (address in metadata) { doUpdateMetadata(address, field, null) @@ -76,7 +76,7 @@ object Service { } } - private fun doUpdateMetadata(address: Address, field: KMutableProperty1, value: String?) { + private fun doUpdateMetadata(address: SnesAddress, field: KMutableProperty1, value: String?) { val line = metadata.getOrCreate(address) field.set(line, value) diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Disassembly.kt b/src/main/java/com/smallhacker/disbrowser/asm/Disassembly.kt index ebbe791..1d8c53d 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Disassembly.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Disassembly.kt @@ -3,7 +3,7 @@ package com.smallhacker.disbrowser.asm class Disassembly(lines: List) : Iterable { override fun iterator() = lineList.iterator() as Iterator - private val knownAddresses = HashSet
() + private val knownAddresses = HashSet() private val lineList = ArrayList() init { @@ -16,5 +16,5 @@ class Disassembly(lines: List) : Iterable { } } - operator fun contains(address: Address) = address in knownAddresses + operator fun contains(address: SnesAddress) = address in knownAddresses } \ No newline at end of file diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Instruction.kt b/src/main/java/com/smallhacker/disbrowser/asm/Instruction.kt index 164cd79..3a7eb20 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Instruction.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Instruction.kt @@ -3,10 +3,10 @@ package com.smallhacker.disbrowser.asm import com.smallhacker.disbrowser.util.* interface CodeUnit { - val address: Address? - val relativeAddress: Address - val presentedAddress: Address - val nextPresentedAddress: Address + val address: SnesAddress? + val relativeAddress: SnesAddress + val presentedAddress: SnesAddress + val nextPresentedAddress: SnesAddress val linkedState: State? val preState: State? @@ -22,6 +22,11 @@ interface CodeUnit { 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 { @@ -58,22 +63,22 @@ interface CodeUnit { class DataBlock( override val opcode: Opcode, override val bytes: RomData, - override val presentedAddress: Address, - override val relativeAddress: Address, + override val presentedAddress: SnesAddress, + override val relativeAddress: SnesAddress, override val linkedState: State? ) : CodeUnit { - override val nextPresentedAddress: Address + override val nextPresentedAddress: SnesAddress get() = presentedAddress + operandLength.toInt() override val operandLength get() = bytes.size - override val address: Address? = null + override val address: SnesAddress? = null override val preState: State? = null override val postState: State? = null override val lengthSuffix: String? = null } 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 presentedAddress get() = 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 get() = opcode.mode.operandLength(preState) - private fun link(): Address? { + private fun link(): SnesAddress? { if (!opcode.link) { return null } return when (opcode.mode) { Mode.ABSOLUTE -> relativeAddress.withinBank(word) - Mode.ABSOLUTE_LONG -> Address(long) + Mode.ABSOLUTE_LONG -> SnesAddress(long) Mode.RELATIVE -> relativeAddress + 2 + signedByte.toInt() Mode.RELATIVE_LONG -> relativeAddress + 3 + signedWord.toInt() Mode.DATA_WORD -> relativeAddress.withinBank(word) - Mode.DATA_LONG -> Address(long) + Mode.DATA_LONG -> SnesAddress(long) else -> null } } diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Metadata.kt b/src/main/java/com/smallhacker/disbrowser/asm/Metadata.kt index ea20d11..412975d 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Metadata.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Metadata.kt @@ -7,36 +7,34 @@ import com.smallhacker.disbrowser.util.toUInt24 import java.util.* class Metadata { - private val lines: MutableMap + @JsonProperty + private val code: TreeMap constructor() { - this.lines = HashMap() + this.code = TreeMap() } @JsonCreator - private constructor(@JsonProperty lines: Map) { - this.lines = HashMap(lines) + private constructor(@JsonProperty code: Map) { + this.code = TreeMap(code) } - @JsonValue - private fun serialize() = TreeMap(lines) - - operator fun set(address: Address, line: MetadataLine?): Metadata { + operator fun set(address: SnesAddress, line: MetadataLine?): Metadata { if (line == null) { - lines.remove(address) + code.remove(address) } else { - lines[address] = line + code[address] = line } return this } - operator fun get(address: Address): MetadataLine? { - return lines[address] + operator fun get(address: SnesAddress): MetadataLine? { + 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] if (line != null) { return line @@ -60,17 +58,17 @@ object NonReturningRoutine : InstructionFlag { } 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 ) : InstructionFlag { private val uEntries get() = entries.toUInt() - fun readTable(data: RomData): Sequence
{ + fun readTable(data: RomData): Sequence { return (0u until uEntries) .asSequence() .map { it + start.pc } .map { pc -> joinBytes(data[pc], data[pc + uEntries], data[pc + uEntries + uEntries]).toUInt24() } - .map { Address(it) } + .map { SnesAddress(it) } } fun generateCode(jumpInstruction: Instruction): Sequence { @@ -92,7 +90,7 @@ class JmpIndirectLongInterleavedTable @JsonCreator constructor( jumpInstruction.postState.address + offset.toInt(), jumpInstruction.relativeAddress, jumpInstruction.opcode.mutate(jumpInstruction) - .mutateAddress { Address(target) } + .mutateAddress { SnesAddress(target) } .withOrigin(jumpInstruction) ) // Instruction( @@ -110,13 +108,13 @@ class JslTableRoutine @JsonCreator constructor( @field:JsonProperty @JsonProperty private val entries: Int ) : InstructionFlag { - fun readTable(postJsr: State): Sequence
{ + fun readTable(postJsr: State): Sequence { val data = postJsr.data return (0u until entries.toUInt()) .asSequence() .map { postJsr.address.pc + (it * 3u) } .map { pc -> joinBytes(data[pc], data[pc + 1u], data[pc + 2u]).toUInt24() } - .map { Address(it) } + .map { SnesAddress(it) } } override fun toString() = "JslTableRoutine($entries)" diff --git a/src/main/java/com/smallhacker/disbrowser/asm/MetadataLine.kt b/src/main/java/com/smallhacker/disbrowser/asm/MetadataLine.kt index e4d13a2..428458f 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/MetadataLine.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/MetadataLine.kt @@ -8,8 +8,9 @@ data class MetadataLine( var label: String? = null, var comment: String? = null, var preComment: String? = null, + var length: Int? = null, val flags: MutableList = ArrayList() ) { @JsonIgnore - fun isEmpty() = (label == null) && (comment == null) && (preComment == null) && (flags.isEmpty()) + fun isEmpty() = (label == null) && (comment == null) && (preComment == null) && (length == 0) && (flags.isEmpty()) } diff --git a/src/main/java/com/smallhacker/disbrowser/asm/MetadataRam.kt b/src/main/java/com/smallhacker/disbrowser/asm/MetadataRam.kt new file mode 100644 index 0000000..6ca1a68 --- /dev/null +++ b/src/main/java/com/smallhacker/disbrowser/asm/MetadataRam.kt @@ -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) +} diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Mnemonic.kt b/src/main/java/com/smallhacker/disbrowser/asm/Mnemonic.kt index c6811d4..0bd0ad0 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Mnemonic.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Mnemonic.kt @@ -1,7 +1,7 @@ package com.smallhacker.disbrowser.asm -enum class Mnemonic(private val nameOverride: String? = null) { - ADC, AND, ASL, BCC, BCS, BEQ, BIT, BMI, BNE, BPL, BRA, +enum class Mnemonic(private val nameOverride: String? = null, val alternativeName: String? = null) { + 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, CPY, DEC, DEX, DEY, EOR, INC, INX, INY, JMP, JML, JSL, 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, WAI, WDM, XBA, XCE, - DB(".db"), DW(".dw"), DL(".dl"); + DB(nameOverride = ".db"), DW(nameOverride = ".dw"), DL(nameOverride = ".dl"); val displayName get() = nameOverride ?: name } diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Segment.kt b/src/main/java/com/smallhacker/disbrowser/asm/Segment.kt index e806a0c..005809a 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Segment.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Segment.kt @@ -1,25 +1,25 @@ package com.smallhacker.disbrowser.asm -class Segment (val start: Address, val end: SegmentEnd, val instructions: List) +class Segment (val start: SnesAddress, val end: SegmentEnd, val instructions: List) -class SegmentEnd(val address: Address, val local: List = emptyList(), val remote: List = emptyList(), val returnAddress: Address? = null, val returning: Boolean = false) +class SegmentEnd(val address: SnesAddress, val local: List = emptyList(), val remote: List = emptyList(), val returnAddress: SnesAddress? = null, val returning: Boolean = false) -fun stoppingSegmentEnd(address: Address) +fun stoppingSegmentEnd(address: SnesAddress) = SegmentEnd(address) -fun branchingSegmentEnd(address: Address, continueState: State, branchState: State) +fun branchingSegmentEnd(address: SnesAddress, continueState: State, branchState: State) = SegmentEnd(address, local = listOf(continueState, branchState)) -fun alwaysBranchingSegmentEnd(address: Address, branchState: State) +fun alwaysBranchingSegmentEnd(address: SnesAddress, branchState: State) = SegmentEnd(address, local = listOf(branchState)) -fun jumpSegmentEnd(address: Address, targetState: State) +fun jumpSegmentEnd(address: SnesAddress, targetState: State) = 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) -fun returnSegmentEnd(address: Address) +fun returnSegmentEnd(address: SnesAddress) = SegmentEnd(address, returning = true) fun continuationSegmentEnd(state: State) diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Address.kt b/src/main/java/com/smallhacker/disbrowser/asm/SnesAddress.kt similarity index 54% rename from src/main/java/com/smallhacker/disbrowser/asm/Address.kt rename to src/main/java/com/smallhacker/disbrowser/asm/SnesAddress.kt index df37416..be01ae0 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Address.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/SnesAddress.kt @@ -1,19 +1,16 @@ package com.smallhacker.disbrowser.asm 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.smallhacker.disbrowser.util.UInt24 import com.smallhacker.disbrowser.util.toUInt24 import com.smallhacker.disbrowser.util.tryParseInt -data class Address(val value: UInt24) : Comparable
{ - val rom = (value and 0x8000u).toUInt() == 0u +data class SnesAddress(val value: UInt24) : Comparable { val pc = snesToPc(value) - operator fun plus(offset: Int) = Address(value + offset) - operator fun minus(offset: Int) = Address(value - offset) + operator fun plus(offset: Int) = SnesAddress(value + offset) + operator fun minus(offset: Int) = SnesAddress(value - offset) operator fun inc() = plus(1) operator fun dec() = minus(1) @@ -22,21 +19,21 @@ data class Address(val value: UInt24) : Comparable
{ @JsonValue 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 { @JvmStatic @JsonCreator - fun parse(address: String): Address? = tryParseInt(address, 16) - ?.let { Address(it.toUInt24()) } + fun parse(address: String): SnesAddress? = tryParseInt(address, 16) + ?.let { SnesAddress(it.toUInt24()) } } } -fun address(snesAddress: Int) = Address(snesAddress.toUInt24()) +fun address(snesAddress: Int) = SnesAddress(snesAddress.toUInt24()) private fun snesToPc(value: UInt24): UInt { // TODO: This is incredibly oversimplified. Anything that isn't a small LoROM will crash and burn diff --git a/src/main/java/com/smallhacker/disbrowser/asm/State.kt b/src/main/java/com/smallhacker/disbrowser/asm/State.kt index 0cb7749..416dbb7 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/State.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/State.kt @@ -2,10 +2,14 @@ package com.smallhacker.disbrowser.asm 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 = immStack()) { +data class State(val origin: Instruction? = null, val data: RomData, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack = immStack()) { val m: Boolean? get() = flags.getBoolean(0x20u) 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 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) - 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 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() } + 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 { return stack.reversed().asSequence() .map { stackByteToString(it) } diff --git a/src/main/java/com/smallhacker/disbrowser/disassembler/Disassembler.kt b/src/main/java/com/smallhacker/disbrowser/disassembler/Disassembler.kt index 66a1a8f..3a38900 100644 --- a/src/main/java/com/smallhacker/disbrowser/disassembler/Disassembler.kt +++ b/src/main/java/com/smallhacker/disbrowser/disassembler/Disassembler.kt @@ -6,7 +6,7 @@ import kotlin.collections.ArrayList object Disassembler { fun disassemble(initialState: State, metadata: Metadata, global: Boolean): Disassembly { - val seen = HashSet
() + val seen = HashSet() val queue = ArrayDeque() fun tryAdd(state: State) { @@ -79,7 +79,7 @@ object Disassembler { } fun disassembleSegments(initialState: State): List { - val mapping = HashMap() + val mapping = HashMap() val queue = ArrayDeque() val segments = ArrayList() @@ -119,12 +119,12 @@ object Disassembler { return segments } - fun disassembleSegment(initialState: State, mapping: MutableMap): Segment { + fun disassembleSegment(initialState: State, mapping: MutableMap): Segment { val instructions = ArrayList() var lastState = initialState val queue = ArrayDeque() - val seen = HashSet
() + val seen = HashSet() fun finalize(segment: Segment): Segment { instructions.forEach { diff --git a/src/main/java/com/smallhacker/disbrowser/resource/DisassemblyResource.kt b/src/main/java/com/smallhacker/disbrowser/resource/DisassemblyResource.kt index 3b0d10a..90d901e 100644 --- a/src/main/java/com/smallhacker/disbrowser/resource/DisassemblyResource.kt +++ b/src/main/java/com/smallhacker/disbrowser/resource/DisassemblyResource.kt @@ -2,7 +2,7 @@ package com.smallhacker.disbrowser.resource import com.smallhacker.disbrowser.HtmlNode import com.smallhacker.disbrowser.Service -import com.smallhacker.disbrowser.asm.Address +import com.smallhacker.disbrowser.asm.SnesAddress import com.smallhacker.disbrowser.asm.VagueNumber import java.nio.charset.StandardCharsets import javax.ws.rs.GET @@ -30,7 +30,7 @@ class DisassemblyResource { @Produces(MediaType.TEXT_HTML) fun getIt(@PathParam("address") address: String, @PathParam("state") state: String): Response { return handle { - Address.parse(address)?.let { + SnesAddress.parse(address)?.let { val flags = parseState(state) Service.showDisassembly(it, flags) } diff --git a/src/main/java/com/smallhacker/disbrowser/resource/RestResource.kt b/src/main/java/com/smallhacker/disbrowser/resource/RestResource.kt index 86df856..6fc9ba8 100644 --- a/src/main/java/com/smallhacker/disbrowser/resource/RestResource.kt +++ b/src/main/java/com/smallhacker/disbrowser/resource/RestResource.kt @@ -1,7 +1,7 @@ package com.smallhacker.disbrowser.resource import com.smallhacker.disbrowser.Service -import com.smallhacker.disbrowser.asm.Address +import com.smallhacker.disbrowser.asm.SnesAddress import com.smallhacker.disbrowser.asm.MetadataLine import javax.ws.rs.Consumes import javax.ws.rs.POST @@ -16,7 +16,7 @@ class RestResource { @Path("{address}/{field}") @Consumes(MediaType.TEXT_PLAIN) 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) { "preComment" -> MetadataLine::preComment "comment" -> MetadataLine::comment diff --git a/src/main/resources/public/style.css b/src/main/resources/public/style.css index 3d86c1b..b04ec89 100644 --- a/src/main/resources/public/style.css +++ b/src/main/resources/public/style.css @@ -99,9 +99,12 @@ tr.line-active { border: none; background: none; padding: 0; - width: 100%; } .field-comment { width: 500px; +} + +.opcode-info{ + text-decoration: green underline dotted; } \ No newline at end of file