diff --git a/Zelda no Densetsu - Kamigami no Triforce (Japan).json b/Zelda no Densetsu - Kamigami no Triforce (Japan).json index 4fbddb6..f39de13 100644 --- a/Zelda no Densetsu - Kamigami no Triforce (Japan).json +++ b/Zelda no Densetsu - Kamigami no Triforce (Japan).json @@ -1,9 +1,23 @@ { "code" : { + "002140" : { + "label" : "apuIo0" + }, + "002141" : { + "label" : "apuIo1" + }, + "002142" : { + "label" : "apuIo2" + }, + "002143" : { + "label" : "apuIo3" + }, + "00420c" : { + "label" : "hdmaEnable" + }, "008000" : { "label" : "ResetVector" }, - "00800a" : { }, "00801b" : { "comment" : "\\ Turn off emulation mode" }, @@ -58,9 +72,17 @@ "entries" : 28 } ] }, + "0080c9" : { + "label" : "NmiVector" + }, + "00822c" : { + "label" : "UnusedVector" + }, + "0082d8" : { + "label" : "IrqVector" + }, "00841e" : { - "label" : "ClearOam", - "comment" : "Test3" + "label" : "ClearOam" }, "00879c" : { "comment" : "Preserve Y value for later", @@ -137,6 +159,9 @@ "00890b" : { "comment" : "/" }, + "00ffff" : { + "label" : "CrashVector" + }, "0287d0" : { "flags" : [ { "flagType" : "JslTableRoutine", @@ -151,6 +176,15 @@ "flagType" : "JslTableRoutine", "entries" : 12 } ] + }, + "7e0010" : { + "label" : "gameMode" + }, + "7e0011" : { + "label" : "subGameMode" + }, + "7e0012" : { + "label" : "nmiExecuted" } } } \ 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 c689cba..7ca482f 100644 --- a/src/main/java/com/smallhacker/disbrowser/Grid.kt +++ b/src/main/java/com/smallhacker/disbrowser/Grid.kt @@ -60,9 +60,6 @@ class Grid { } fun add(ins: CodeUnit, metadata: Metadata, disassembly: Disassembly) { - val insMetadata = ins.address?.let { metadata[it] } - - val actualAddress = ins.address val presentedAddress = ins.presentedAddress if (nextAddress != null) { @@ -75,18 +72,31 @@ class Grid { val y = (height++) addresses[presentedAddress] = y - add(y, ins.address, - text(actualAddress?.toFormattedString() ?: ""), - text(ins.bytesToString()), - editableField(presentedAddress, "label", insMetadata?.label), - fragment { - opcodeNode(ins) - text(" ") - var operands = ins.printOperands() + val (address, bytes, label, primaryMnemonic, secondaryMnemonic, suffix, operands, state, comment, labelAddress) + = ins.print(metadata) + 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 if (link == null) { - text(operands) + if (labelAddress == null) { + text(operands ?: "") + } else { + val currentLabel = metadata[labelAddress]?.label + editablePopupField(labelAddress, "label", operands, currentLabel) + } } else { val local = link.address in disassembly @@ -95,15 +105,15 @@ class Grid { else -> "/${link.address.toSimpleString()}/${link.urlString}" } - operands = metadata[link.address]?.label ?: operands + //operands = metadata[link.address]?.label ?: operands a { - text(operands) + text(operands ?: "") }.attr("href", url) } }, - text(ins.postState?.toString() ?: ""), - editableField(presentedAddress, "comment", insMetadata?.comment) + text(state ?: ""), + editableField(presentedAddress, "comment", comment) ) 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 { return input(value = value ?: "") .addClass("field-$type") @@ -131,6 +129,18 @@ class Grid { .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() { val y = (height++) add(y, null, null, null, null, text("..."), null, null) diff --git a/src/main/java/com/smallhacker/disbrowser/Service.kt b/src/main/java/com/smallhacker/disbrowser/Service.kt index 7412ec1..cc56e6e 100644 --- a/src/main/java/com/smallhacker/disbrowser/Service.kt +++ b/src/main/java/com/smallhacker/disbrowser/Service.kt @@ -9,6 +9,21 @@ import kotlin.reflect.KMutableProperty1 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 { private const val romName = "Zelda no Densetsu - Kamigami no Triforce (Japan)" private val romDir = Paths.get("""P:\Emulation\ROMs\SNES""") @@ -30,7 +45,7 @@ object Service { } 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) return print(disassembly, metadata) @@ -52,17 +67,7 @@ object Service { .sortedBy { it.first distanceTo it.second } .forEach { grid.arrow(it.first, it.second) } - return html { - 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") - } - } + return grid.output() } fun updateMetadata(address: SnesAddress, field: KMutableProperty1, value: String) { @@ -79,11 +84,17 @@ object Service { val line = metadata.getOrCreate(address) field.set(line, value) - if (line.isEmpty()) { - metadata[address] = null - } - + metadata.cleanUp() 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) \ 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 1f9a6ca..223bf3a 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Instruction.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Instruction.kt @@ -16,18 +16,9 @@ interface CodeUnit { val opcode: Opcode val lengthSuffix: String? + val memory: SnesMapper + 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 { return bytes.asSequence() @@ -65,7 +56,8 @@ class DataBlock( override val bytes: ValidMemorySpace, override val presentedAddress: SnesAddress, override val relativeAddress: SnesAddress, - override val linkedState: State? + override val linkedState: State?, + override val memory: SnesMapper ) : CodeUnit { override val nextPresentedAddress: SnesAddress 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 { + override val memory = preState.memory override val address: SnesAddress get() = preState.address override val relativeAddress get() = address override val presentedAddress get() = address @@ -92,9 +85,11 @@ class Instruction(override val bytes: ValidMemorySpace, override val opcode: Opc .withOrigin(this) } + private val showLengthSuffix get() = opcode.mode.showLengthSuffix and opcode.mnemonic.showLengthSuffix + override val lengthSuffix: String? get() { - if (!opcode.mode.showLengthSuffix) { + if (!showLengthSuffix) { return null } @@ -116,58 +111,55 @@ class Instruction(override val bytes: ValidMemorySpace, override val opcode: Opc } 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? { - 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() - } - } + private fun referencedAddress() = opcode.mode.referencedAddress(this) 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)" } -} \ No newline at end of file +} + +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? +) \ No newline at end of file diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Metadata.kt b/src/main/java/com/smallhacker/disbrowser/asm/Metadata.kt index c924780..0423576 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Metadata.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Metadata.kt @@ -6,12 +6,13 @@ import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonSubTypes.Type import com.fasterxml.jackson.annotation.JsonTypeInfo import com.smallhacker.disbrowser.util.joinNullableBytes +import com.smallhacker.disbrowser.util.removeIf import com.smallhacker.disbrowser.util.toUInt24 import java.util.* class Metadata { @JsonProperty - private val code: TreeMap + private val code: MutableMap constructor() { this.code = TreeMap() @@ -46,6 +47,10 @@ class Metadata { this[address] = newLine return newLine } + + fun cleanUp() { + code.removeIf { _, v -> v.isEmpty() } + } } @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "flagType") @@ -88,13 +93,14 @@ class JmpIndirectLongInterleavedTable @JsonCreator constructor( val target = table.getLong(offset) DataBlock( - Opcode.POINTER_LONG, + Opcode.CODE_POINTER_LONG, table.range(offset, 3u), jumpInstruction.postState.address + offset.toInt(), jumpInstruction.relativeAddress, jumpInstruction.opcode.mutate(jumpInstruction) .mutateAddress { SnesAddress(target) } - .withOrigin(jumpInstruction) + .withOrigin(jumpInstruction), + jumpInstruction.memory ) } } diff --git a/src/main/java/com/smallhacker/disbrowser/asm/MetadataLine.kt b/src/main/java/com/smallhacker/disbrowser/asm/MetadataLine.kt index 428458f..c76d6ab 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/MetadataLine.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/MetadataLine.kt @@ -12,5 +12,5 @@ data class MetadataLine( val flags: MutableList = ArrayList() ) { @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()) } diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Mnemonic.kt b/src/main/java/com/smallhacker/disbrowser/asm/Mnemonic.kt index ec45b70..5b23178 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Mnemonic.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Mnemonic.kt @@ -1,10 +1,10 @@ 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, 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, + CPY, DEC, DEX, DEY, EOR, INC, INX, INY, JMP(showLengthSuffix = false), JML(showLengthSuffix = false), JSL(showLengthSuffix = false), + JSR(showLengthSuffix = false), LDA, LDX, LDY, LSR, MVN, MVP, NOP, ORA, PEA, PEI, PER, PHA, PHB, PHD, PHK, PHP, PHX, PHY, PLA, PLB, PLD, PLP, PLX, PLY, REP, ROL, ROR, RTI, RTL, RTS, SBC, SEC, SED, SEI, SEP, STA, STP, STX, STY, STZ, TAX, TAY, TCD, diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Mode.kt b/src/main/java/com/smallhacker/disbrowser/asm/Mode.kt index f15b98e..77eb026 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Mode.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Mode.kt @@ -2,144 +2,228 @@ package com.smallhacker.disbrowser.asm import com.smallhacker.disbrowser.util.* -private val ZEROES = Regex("[0]+") -private fun countBytes(format: String) = (ZEROES.find(format)?.groupValues?.firstOrNull()?.length?.toUInt() ?: 0u) / 2u - -private const val ACCUMULATOR_SIZE = -1 -private const val INDEX_SIZE = -2 - -fun format(format: String, value: UInt, operandBytes: UInt = countBytes(format)) = - format.replace(ZEROES, toHex(value, operandBytes)) - -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() - } - } +interface Mode { + val dataMode get() = false + val showLengthSuffix get() = true + val canHaveLabel: Boolean + fun operandLength(state: State): UInt? + fun printWithLabel(ins: CodeUnit, metadata: Metadata): String? = referencedAddress(ins)?.let { metadata[it]?.label } + fun printRaw(ins: CodeUnit): String + fun referencedAddress(ins: CodeUnit): SnesAddress? } + +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 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 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, "[", "]") diff --git a/src/main/java/com/smallhacker/disbrowser/asm/Opcode.kt b/src/main/java/com/smallhacker/disbrowser/asm/Opcode.kt index de94a5b..0c03447 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/Opcode.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/Opcode.kt @@ -3,7 +3,6 @@ package com.smallhacker.disbrowser.asm import java.util.HashMap import com.smallhacker.disbrowser.asm.Mnemonic.* -import com.smallhacker.disbrowser.asm.Mode.* typealias SegmentEnder = Instruction.() -> SegmentEnd? @@ -46,12 +45,13 @@ class Opcode private constructor(val mnemonic: Mnemonic, val mode: Mode, val end } companion object { - val DATA_BYTE = Opcode(Mnemonic.DB, Mode.DATA_BYTE, { null }, { it.preState }) - val DATA_WORD = Opcode(Mnemonic.DW, Mode.DATA_WORD, { null }, { it.preState }) - val DATA_LONG = Opcode(Mnemonic.DL, Mode.DATA_LONG, { null }, { it.preState }) - - val POINTER_WORD = Opcode(Mnemonic.DW, Mode.CODE_WORD, { null }, { it.preState }).linking() - val POINTER_LONG = Opcode(Mnemonic.DL, Mode.CODE_LONG, { null }, { it.preState }).linking() + val DATA_BYTE = Opcode(Mnemonic.DB, DirectData.byte, { null }, { it.preState }) + val DATA_WORD = Opcode(Mnemonic.DW, DirectData.word, { 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 DATA_POINTER_LONG = Opcode(Mnemonic.DL, DataPointer.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 @@ -80,284 +80,284 @@ class Opcode private constructor(val mnemonic: Mnemonic, val mode: Mode, val end val dynamicSubJumping: SegmentEnder = { stoppingSegmentEnd(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(0x02, COP, IMMEDIATE_8, alwaysStop).stop() - add(0x42, WDM, IMMEDIATE_8, alwaysStop).stop() + add(0x00, BRK, Immediate8, alwaysStop).stop() + add(0x02, COP, Immediate8, 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(0xCB, WAI, IMPLIED, alwaysContinue) + add(0xDB, STP, Implied, alwaysStop).stop() + add(0xCB, WAI, Implied, alwaysContinue) - add(0x10, BPL, RELATIVE, branching).branching() - add(0x30, BMI, RELATIVE, branching).branching() - add(0x50, BVC, RELATIVE, branching).branching() - add(0x70, BVS, RELATIVE, branching).branching() - add(0x80, BRA, RELATIVE, alwaysBranching).stop().branching() - add(0x90, BCC, RELATIVE, branching).branching() - add(0xB0, BCS, RELATIVE, branching).branching() - add(0xD0, BNE, RELATIVE, branching).branching() - add(0xF0, BEQ, RELATIVE, branching).branching() - add(0x82, BRL, RELATIVE_LONG, alwaysBranching).stop().branching() + add(0x10, BPL, Relative, branching).branching() + add(0x30, BMI, Relative, branching).branching() + add(0x50, BVC, Relative, branching).branching() + add(0x70, BVS, Relative, branching).branching() + add(0x80, BRA, Relative, alwaysBranching).stop().branching() + add(0x90, BCC, Relative, branching).branching() + add(0xB0, BCS, Relative, branching).branching() + add(0xD0, BNE, Relative, branching).branching() + add(0xF0, BEQ, Relative, branching).branching() + add(0x82, BRL, RelativeLong, alwaysBranching).stop().branching() - add(0x4C, JMP, ABSOLUTE_CODE, jumping).linking().stop() - add(0x5C, JML, ABSOLUTE_LONG, jumping).linking().stop() - add(0x6C, JMP, ABSOLUTE_INDIRECT, dynamicJumping).stop() - add(0x7C, JMP, ABSOLUTE_X_INDIRECT, dynamicJumping).stop() - add(0xDC, JMP, ABSOLUTE_INDIRECT_LONG, dynamicJumping).stop() + add(0x4C, JMP, AbsoluteCode, jumping).linking().stop() + add(0x5C, JML, AbsoluteLong, jumping).linking().stop() + add(0x6C, JMP, Absolute.indirect, dynamicJumping).stop() + add(0x7C, JMP, Absolute.x.indirect, dynamicJumping).stop() + add(0xDC, JMP, Absolute.indirectLong, dynamicJumping).stop() - add(0x22, JSL, ABSOLUTE_LONG, subJumping).linking().mayStop() - add(0x20, JSR, ABSOLUTE_CODE, subJumping).linking().mayStop() - add(0xFC, JSR, ABSOLUTE_X_INDIRECT, dynamicSubJumping).mayStop() + add(0x22, JSL, AbsoluteLong, subJumping).linking().mayStop() + add(0x20, JSR, AbsoluteCode, subJumping).linking().mayStop() + add(0xFC, JSR, Absolute.x.indirect, dynamicSubJumping).mayStop() - add(0x60, RTS, IMPLIED, returning).stop() - add(0x6B, RTL, IMPLIED, returning).stop() - add(0x40, RTI, IMPLIED, returning).stop() + add(0x60, RTS, Implied, returning).stop() + add(0x6B, RTL, Implied, returning).stop() + add(0x40, RTI, Implied, returning).stop() - add(0x1B, TCS, IMPLIED, alwaysContinue) - add(0x3B, TSC, IMPLIED, alwaysContinue) - add(0x5B, TCD, IMPLIED, alwaysContinue) - add(0x7B, TDC, IMPLIED, alwaysContinue) - add(0xAA, TAX, IMPLIED, alwaysContinue) - add(0xA8, TAY, IMPLIED, alwaysContinue) - add(0xBA, TSX, IMPLIED, alwaysContinue) - add(0x8A, TXA, IMPLIED, alwaysContinue) - add(0x9A, TXS, IMPLIED, alwaysContinue) - add(0x9B, TXY, IMPLIED, alwaysContinue) - add(0x98, TYA, IMPLIED, alwaysContinue) - add(0xBB, TYX, IMPLIED, alwaysContinue) - add(0xEB, XBA, IMPLIED, alwaysContinue) + add(0x1B, TCS, Implied, alwaysContinue) + add(0x3B, TSC, Implied, alwaysContinue) + add(0x5B, TCD, Implied, alwaysContinue) + add(0x7B, TDC, Implied, alwaysContinue) + add(0xAA, TAX, Implied, alwaysContinue) + add(0xA8, TAY, Implied, alwaysContinue) + add(0xBA, TSX, Implied, alwaysContinue) + add(0x8A, TXA, Implied, alwaysContinue) + add(0x9A, TXS, Implied, alwaysContinue) + add(0x9B, TXY, Implied, alwaysContinue) + add(0x98, TYA, Implied, alwaysContinue) + add(0xBB, TYX, Implied, alwaysContinue) + add(0xEB, XBA, Implied, alwaysContinue) - add(0x18, CLC, IMPLIED, alwaysContinue) - add(0x38, SEC, IMPLIED, alwaysContinue) - add(0x58, CLI, IMPLIED, alwaysContinue) - add(0x78, SEI, IMPLIED, alwaysContinue) - add(0xF8, SED, IMPLIED, alwaysContinue) - add(0xD8, CLD, IMPLIED, alwaysContinue) - add(0xB8, CLV, IMPLIED, alwaysContinue) - add(0xE2, SEP, IMMEDIATE_8, alwaysContinue) { it.preState.sep(it.bytes[1u]) } - add(0xC2, REP, IMMEDIATE_8, alwaysContinue) { it.preState.rep(it.bytes[1u]) } - add(0xFB, XCE, IMPLIED, alwaysContinue) + add(0x18, CLC, Implied, alwaysContinue) + add(0x38, SEC, Implied, alwaysContinue) + add(0x58, CLI, Implied, alwaysContinue) + add(0x78, SEI, Implied, alwaysContinue) + add(0xF8, SED, Implied, alwaysContinue) + add(0xD8, CLD, Implied, alwaysContinue) + add(0xB8, CLV, Implied, alwaysContinue) + add(0xE2, SEP, Immediate8, alwaysContinue) { it.preState.sep(it.bytes[1u]) } + add(0xC2, REP, Immediate8, alwaysContinue) { it.preState.rep(it.bytes[1u]) } + add(0xFB, XCE, Implied, alwaysContinue) - add(0xC1, CMP, DIRECT_X_INDIRECT, alwaysContinue) - add(0xC3, CMP, DIRECT_S, alwaysContinue) - add(0xC5, CMP, DIRECT, alwaysContinue) - add(0xC7, CMP, DIRECT_INDIRECT_LONG, alwaysContinue) - add(0xC9, CMP, IMMEDIATE_M, alwaysContinue) - add(0xCD, CMP, ABSOLUTE, alwaysContinue) - add(0xCF, CMP, ABSOLUTE_LONG, alwaysContinue) - add(0xD1, CMP, DIRECT_INDIRECT_Y, alwaysContinue) - add(0xD2, CMP, DIRECT_INDIRECT, alwaysContinue) - add(0xD3, CMP, DIRECT_S_INDIRECT_Y, alwaysContinue) - add(0xD5, CMP, DIRECT_X, alwaysContinue) - add(0xD7, CMP, DIRECT_INDIRECT_LONG_Y, alwaysContinue) - add(0xD9, CMP, ABSOLUTE_Y, alwaysContinue) - add(0xDD, CMP, ABSOLUTE_X, alwaysContinue) - add(0xDF, CMP, ABSOLUTE_LONG_X, alwaysContinue) - add(0xE0, CPX, IMMEDIATE_X, alwaysContinue) - add(0xE4, CPX, DIRECT, alwaysContinue) - add(0xEC, CPX, ABSOLUTE, alwaysContinue) - add(0xC0, CPY, IMMEDIATE_X, alwaysContinue) - add(0xC4, CPY, DIRECT, alwaysContinue) - add(0xCC, CPY, ABSOLUTE, alwaysContinue) + add(0xC1, CMP, Direct.x.indirect, alwaysContinue) + add(0xC3, CMP, Direct.s, alwaysContinue) + add(0xC5, CMP, Direct, alwaysContinue) + add(0xC7, CMP, Direct.indirectLong, alwaysContinue) + add(0xC9, CMP, ImmediateM, alwaysContinue) + add(0xCD, CMP, Absolute, alwaysContinue) + add(0xCF, CMP, AbsoluteLong, alwaysContinue) + add(0xD1, CMP, Direct.indirect.y, alwaysContinue) + add(0xD2, CMP, Direct.indirect, alwaysContinue) + add(0xD3, CMP, Direct.s.indirect.y, alwaysContinue) + add(0xD5, CMP, Direct.x, alwaysContinue) + add(0xD7, CMP, Direct.indirectLong.y, alwaysContinue) + add(0xD9, CMP, Absolute.y, alwaysContinue) + add(0xDD, CMP, Absolute.x, alwaysContinue) + add(0xDF, CMP, AbsoluteLong.x, alwaysContinue) + add(0xE0, CPX, ImmediateX, alwaysContinue) + add(0xE4, CPX, Direct, alwaysContinue) + add(0xEC, CPX, Absolute, alwaysContinue) + add(0xC0, CPY, ImmediateX, alwaysContinue) + add(0xC4, CPY, Direct, alwaysContinue) + add(0xCC, CPY, Absolute, alwaysContinue) - add(0xA1, LDA, DIRECT_X_INDIRECT, alwaysContinue) - add(0xA3, LDA, DIRECT_S, alwaysContinue) - add(0xA5, LDA, DIRECT, alwaysContinue) - add(0xA7, LDA, DIRECT_INDIRECT_LONG, alwaysContinue) - add(0xA9, LDA, IMMEDIATE_M, alwaysContinue) - add(0xAD, LDA, ABSOLUTE, alwaysContinue) - add(0xAF, LDA, ABSOLUTE_LONG, alwaysContinue) - add(0xB1, LDA, DIRECT_INDIRECT_Y, alwaysContinue) - add(0xB2, LDA, DIRECT_INDIRECT, alwaysContinue) - add(0xB3, LDA, DIRECT_S_INDIRECT_Y, alwaysContinue) - add(0xB5, LDA, DIRECT_X, alwaysContinue) - add(0xB7, LDA, DIRECT_INDIRECT_LONG_Y, alwaysContinue) - add(0xB9, LDA, ABSOLUTE_Y, alwaysContinue) - add(0xBD, LDA, ABSOLUTE_X, alwaysContinue) - add(0xBF, LDA, ABSOLUTE_LONG_X, alwaysContinue) - add(0xA2, LDX, IMMEDIATE_X, alwaysContinue) - add(0xA6, LDX, DIRECT, alwaysContinue) - add(0xAE, LDX, ABSOLUTE, alwaysContinue) - add(0xB6, LDX, DIRECT_Y, alwaysContinue) - add(0xBE, LDX, ABSOLUTE_Y, alwaysContinue) - add(0xA0, LDY, IMMEDIATE_X, alwaysContinue) - add(0xA4, LDY, DIRECT, alwaysContinue) - add(0xAC, LDY, ABSOLUTE, alwaysContinue) - add(0xB4, LDY, DIRECT_X, alwaysContinue) - add(0xBC, LDY, ABSOLUTE_X, alwaysContinue) - add(0x81, STA, DIRECT_X_INDIRECT, alwaysContinue) - add(0x83, STA, DIRECT_S, alwaysContinue) - add(0x85, STA, DIRECT, alwaysContinue) - add(0x87, STA, DIRECT_INDIRECT_LONG, alwaysContinue) - add(0x8D, STA, ABSOLUTE, alwaysContinue) - add(0x8F, STA, ABSOLUTE_LONG, alwaysContinue) - add(0x91, STA, DIRECT_INDIRECT_Y, alwaysContinue) - add(0x92, STA, DIRECT_INDIRECT, alwaysContinue) - add(0x93, STA, DIRECT_S_INDIRECT_Y, alwaysContinue) - add(0x95, STA, DIRECT_X, alwaysContinue) - add(0x97, STA, DIRECT_INDIRECT_LONG_Y, alwaysContinue) - add(0x99, STA, ABSOLUTE_Y, alwaysContinue) - add(0x9D, STA, ABSOLUTE_X, alwaysContinue) - add(0x9F, STA, ABSOLUTE_LONG_X, alwaysContinue) - add(0x86, STX, DIRECT, alwaysContinue) - add(0x8E, STX, ABSOLUTE, alwaysContinue) - add(0x96, STX, DIRECT_Y, alwaysContinue) - add(0x84, STY, DIRECT, alwaysContinue) - add(0x8C, STY, ABSOLUTE, alwaysContinue) - add(0x94, STY, DIRECT_X, alwaysContinue) - add(0x64, STZ, DIRECT, alwaysContinue) - add(0x74, STZ, DIRECT_X, alwaysContinue) - add(0x9C, STZ, ABSOLUTE, alwaysContinue) - add(0x9E, STZ, ABSOLUTE_X, alwaysContinue) + add(0xA1, LDA, Direct.x.indirect, alwaysContinue) + add(0xA3, LDA, Direct.s, alwaysContinue) + add(0xA5, LDA, Direct, alwaysContinue) + add(0xA7, LDA, Direct.indirectLong, alwaysContinue) + add(0xA9, LDA, ImmediateM, alwaysContinue) + add(0xAD, LDA, Absolute, alwaysContinue) + add(0xAF, LDA, AbsoluteLong, alwaysContinue) + add(0xB1, LDA, Direct.indirect.y, alwaysContinue) + add(0xB2, LDA, Direct.indirect, alwaysContinue) + add(0xB3, LDA, Direct.s.indirect.y, alwaysContinue) + add(0xB5, LDA, Direct.x, alwaysContinue) + add(0xB7, LDA, Direct.indirectLong.y, alwaysContinue) + add(0xB9, LDA, Absolute.y, alwaysContinue) + add(0xBD, LDA, Absolute.x, alwaysContinue) + add(0xBF, LDA, AbsoluteLong.x, alwaysContinue) + add(0xA2, LDX, ImmediateX, alwaysContinue) + add(0xA6, LDX, Direct, alwaysContinue) + add(0xAE, LDX, Absolute, alwaysContinue) + add(0xB6, LDX, Direct.y, alwaysContinue) + add(0xBE, LDX, Absolute.y, alwaysContinue) + add(0xA0, LDY, ImmediateX, alwaysContinue) + add(0xA4, LDY, Direct, alwaysContinue) + add(0xAC, LDY, Absolute, alwaysContinue) + add(0xB4, LDY, Direct.x, alwaysContinue) + add(0xBC, LDY, Absolute.x, alwaysContinue) + add(0x81, STA, Direct.x.indirect, alwaysContinue) + add(0x83, STA, Direct.s, alwaysContinue) + add(0x85, STA, Direct, alwaysContinue) + add(0x87, STA, Direct.indirectLong, alwaysContinue) + add(0x8D, STA, Absolute, alwaysContinue) + add(0x8F, STA, AbsoluteLong, alwaysContinue) + add(0x91, STA, Direct.indirect.y, alwaysContinue) + add(0x92, STA, Direct.indirect, alwaysContinue) + add(0x93, STA, Direct.s.indirect.y, alwaysContinue) + add(0x95, STA, Direct.x, alwaysContinue) + add(0x97, STA, Direct.indirectLong.y, alwaysContinue) + add(0x99, STA, Absolute.y, alwaysContinue) + add(0x9D, STA, Absolute.x, alwaysContinue) + add(0x9F, STA, AbsoluteLong.x, alwaysContinue) + add(0x86, STX, Direct, alwaysContinue) + add(0x8E, STX, Absolute, alwaysContinue) + add(0x96, STX, Direct.y, alwaysContinue) + add(0x84, STY, Direct, alwaysContinue) + add(0x8C, STY, Absolute, alwaysContinue) + add(0x94, STY, Direct.x, alwaysContinue) + add(0x64, STZ, Direct, alwaysContinue) + add(0x74, STZ, Direct.x, alwaysContinue) + add(0x9C, STZ, Absolute, alwaysContinue) + add(0x9E, STZ, Absolute.x, alwaysContinue) - add(0x48, PHA, IMPLIED, alwaysContinue) { it.preState.pushUnknown(it.preState.mWidth) } - add(0xDA, PHX, IMPLIED, alwaysContinue) { it.preState.pushUnknown(it.preState.xWidth) } - add(0x5A, PHY, IMPLIED, alwaysContinue) { it.preState.pushUnknown(it.preState.xWidth) } - add(0x68, PLA, IMPLIED, alwaysContinue) { it.preState.pull(it.preState.mWidth) } - add(0xFA, PLX, IMPLIED, alwaysContinue) { it.preState.pull(it.preState.xWidth) } - add(0x7A, PLY, IMPLIED, alwaysContinue) { it.preState.pull(it.preState.xWidth) } + add(0x48, PHA, Implied, alwaysContinue) { it.preState.pushUnknown(it.preState.mWidth) } + add(0xDA, PHX, Implied, alwaysContinue) { it.preState.pushUnknown(it.preState.xWidth) } + add(0x5A, PHY, Implied, alwaysContinue) { it.preState.pushUnknown(it.preState.xWidth) } + add(0x68, PLA, Implied, alwaysContinue) { it.preState.pull(it.preState.mWidth) } + add(0xFA, PLX, Implied, alwaysContinue) { it.preState.pull(it.preState.xWidth) } + add(0x7A, PLY, Implied, alwaysContinue) { it.preState.pull(it.preState.xWidth) } - add(0x8B, PHB, IMPLIED, alwaysContinue) { it.preState.pushUnknown(1u) } - add(0xAB, PLB, IMPLIED, alwaysContinue) { it.preState.pull(1u) } - add(0x0B, PHD, IMPLIED, alwaysContinue) { it.preState.pushUnknown(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(0x08, PHP, IMPLIED, alwaysContinue) { it.preState.push(it.preState.flags) } - add(0x28, PLP, IMPLIED, alwaysContinue) { it.preState.pull { copy(flags = it) } } + add(0x8B, PHB, Implied, alwaysContinue) { it.preState.pushUnknown(1u) } + add(0xAB, PLB, Implied, alwaysContinue) { it.preState.pull(1u) } + add(0x0B, PHD, Implied, alwaysContinue) { it.preState.pushUnknown(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(0x08, PHP, Implied, alwaysContinue) { it.preState.push(it.preState.flags) } + add(0x28, PLP, Implied, alwaysContinue) { it.preState.pull { copy(flags = it) } } - add(0x3A, DEC, IMPLIED, alwaysContinue) - add(0xC6, DEC, DIRECT, alwaysContinue) - add(0xCE, DEC, ABSOLUTE, alwaysContinue) - add(0xD6, DEC, DIRECT_X, alwaysContinue) - add(0xDE, DEC, ABSOLUTE_X, alwaysContinue) - add(0xCA, DEX, IMPLIED, alwaysContinue) - add(0x88, DEY, IMPLIED, alwaysContinue) - add(0x1A, INC, IMPLIED, alwaysContinue) - add(0xE6, INC, DIRECT, alwaysContinue) - add(0xEE, INC, ABSOLUTE, alwaysContinue) - add(0xF6, INC, DIRECT_X, alwaysContinue) - add(0xFE, INC, ABSOLUTE_X, alwaysContinue) - add(0xE8, INX, IMPLIED, alwaysContinue) - add(0xC8, INY, IMPLIED, alwaysContinue) + add(0x3A, DEC, Implied, alwaysContinue) + add(0xC6, DEC, Direct, alwaysContinue) + add(0xCE, DEC, Absolute, alwaysContinue) + add(0xD6, DEC, Direct.x, alwaysContinue) + add(0xDE, DEC, Absolute.x, alwaysContinue) + add(0xCA, DEX, Implied, alwaysContinue) + add(0x88, DEY, Implied, alwaysContinue) + add(0x1A, INC, Implied, alwaysContinue) + add(0xE6, INC, Direct, alwaysContinue) + add(0xEE, INC, Absolute, alwaysContinue) + add(0xF6, INC, Direct.x, alwaysContinue) + add(0xFE, INC, Absolute.x, alwaysContinue) + add(0xE8, INX, Implied, alwaysContinue) + add(0xC8, INY, Implied, alwaysContinue) - add(0x06, ASL, DIRECT, alwaysContinue) - add(0x0A, ASL, IMPLIED, alwaysContinue) - add(0x0E, ASL, ABSOLUTE, alwaysContinue) - add(0x16, ASL, DIRECT_X, alwaysContinue) - add(0x1E, ASL, ABSOLUTE_X, alwaysContinue) - add(0x46, LSR, DIRECT, alwaysContinue) - add(0x4A, LSR, IMPLIED, alwaysContinue) - add(0x4E, LSR, ABSOLUTE, alwaysContinue) - add(0x56, LSR, DIRECT_X, alwaysContinue) - add(0x5E, LSR, ABSOLUTE_X, alwaysContinue) - add(0x26, ROL, DIRECT, alwaysContinue) - add(0x2A, ROL, IMPLIED, alwaysContinue) - add(0x2E, ROL, ABSOLUTE, alwaysContinue) - add(0x36, ROL, DIRECT_X, alwaysContinue) - add(0x3E, ROL, ABSOLUTE_X, alwaysContinue) - add(0x66, ROR, DIRECT, alwaysContinue) - add(0x6A, ROR, IMPLIED, alwaysContinue) - add(0x6E, ROR, ABSOLUTE, alwaysContinue) - add(0x76, ROR, DIRECT_X, alwaysContinue) - add(0x7E, ROR, ABSOLUTE_X, alwaysContinue) + add(0x06, ASL, Direct, alwaysContinue) + add(0x0A, ASL, Implied, alwaysContinue) + add(0x0E, ASL, Absolute, alwaysContinue) + add(0x16, ASL, Direct.x, alwaysContinue) + add(0x1E, ASL, Absolute.x, alwaysContinue) + add(0x46, LSR, Direct, alwaysContinue) + add(0x4A, LSR, Implied, alwaysContinue) + add(0x4E, LSR, Absolute, alwaysContinue) + add(0x56, LSR, Direct.x, alwaysContinue) + add(0x5E, LSR, Absolute.x, alwaysContinue) + add(0x26, ROL, Direct, alwaysContinue) + add(0x2A, ROL, Implied, alwaysContinue) + add(0x2E, ROL, Absolute, alwaysContinue) + add(0x36, ROL, Direct.x, alwaysContinue) + add(0x3E, ROL, Absolute.x, alwaysContinue) + add(0x66, ROR, Direct, alwaysContinue) + add(0x6A, ROR, Implied, alwaysContinue) + add(0x6E, ROR, Absolute, alwaysContinue) + add(0x76, ROR, Direct.x, alwaysContinue) + add(0x7E, ROR, Absolute.x, alwaysContinue) - add(0x61, ADC, DIRECT_X_INDIRECT, alwaysContinue) - add(0x63, ADC, DIRECT_S, alwaysContinue) - add(0x65, ADC, DIRECT, alwaysContinue) - add(0x67, ADC, DIRECT_INDIRECT_LONG, alwaysContinue) - add(0x69, ADC, IMMEDIATE_M, alwaysContinue) - add(0x6D, ADC, ABSOLUTE, alwaysContinue) - add(0x6F, ADC, ABSOLUTE_LONG, alwaysContinue) - add(0x71, ADC, DIRECT_INDIRECT_Y, alwaysContinue) - add(0x72, ADC, DIRECT_INDIRECT, alwaysContinue) - add(0x73, ADC, DIRECT_S_INDIRECT_Y, alwaysContinue) - add(0x75, ADC, DIRECT_X, alwaysContinue) - add(0x77, ADC, DIRECT_INDIRECT_LONG_Y, alwaysContinue) - add(0x79, ADC, ABSOLUTE_Y, alwaysContinue) - add(0x7D, ADC, ABSOLUTE_X, alwaysContinue) - add(0x7F, ADC, ABSOLUTE_LONG_X, alwaysContinue) - add(0xE1, SBC, DIRECT_X_INDIRECT, alwaysContinue) - add(0xE3, SBC, DIRECT_S, alwaysContinue) - add(0xE5, SBC, DIRECT, alwaysContinue) - add(0xE7, SBC, DIRECT_INDIRECT_LONG, alwaysContinue) - add(0xE9, SBC, IMMEDIATE_M, alwaysContinue) - add(0xED, SBC, ABSOLUTE, alwaysContinue) - add(0xEF, SBC, ABSOLUTE_LONG, alwaysContinue) - add(0xF1, SBC, DIRECT_INDIRECT_Y, alwaysContinue) - add(0xF2, SBC, DIRECT_INDIRECT, alwaysContinue) - add(0xF3, SBC, DIRECT_S_INDIRECT_Y, alwaysContinue) - add(0xF5, SBC, DIRECT_X, alwaysContinue) - add(0xF7, SBC, DIRECT_INDIRECT_LONG_Y, alwaysContinue) - add(0xF9, SBC, ABSOLUTE_Y, alwaysContinue) - add(0xFD, SBC, ABSOLUTE_X, alwaysContinue) - add(0xFF, SBC, ABSOLUTE_LONG_X, alwaysContinue) + add(0x61, ADC, Direct.x.indirect, alwaysContinue) + add(0x63, ADC, Direct.s, alwaysContinue) + add(0x65, ADC, Direct, alwaysContinue) + add(0x67, ADC, Direct.indirectLong, alwaysContinue) + add(0x69, ADC, ImmediateM, alwaysContinue) + add(0x6D, ADC, Absolute, alwaysContinue) + add(0x6F, ADC, AbsoluteLong, alwaysContinue) + add(0x71, ADC, Direct.indirect.y, alwaysContinue) + add(0x72, ADC, Direct.indirect, alwaysContinue) + add(0x73, ADC, Direct.s.indirect.y, alwaysContinue) + add(0x75, ADC, Direct.x, alwaysContinue) + add(0x77, ADC, Direct.indirectLong.y, alwaysContinue) + add(0x79, ADC, Absolute.y, alwaysContinue) + add(0x7D, ADC, Absolute.x, alwaysContinue) + add(0x7F, ADC, AbsoluteLong.x, alwaysContinue) + add(0xE1, SBC, Direct.x.indirect, alwaysContinue) + add(0xE3, SBC, Direct.s, alwaysContinue) + add(0xE5, SBC, Direct, alwaysContinue) + add(0xE7, SBC, Direct.indirectLong, alwaysContinue) + add(0xE9, SBC, ImmediateM, alwaysContinue) + add(0xED, SBC, Absolute, alwaysContinue) + add(0xEF, SBC, AbsoluteLong, alwaysContinue) + add(0xF1, SBC, Direct.indirect.y, alwaysContinue) + add(0xF2, SBC, Direct.indirect, alwaysContinue) + add(0xF3, SBC, Direct.s.indirect.y, alwaysContinue) + add(0xF5, SBC, Direct.x, alwaysContinue) + add(0xF7, SBC, Direct.indirectLong.y, alwaysContinue) + add(0xF9, SBC, Absolute.y, alwaysContinue) + add(0xFD, SBC, Absolute.x, alwaysContinue) + add(0xFF, SBC, AbsoluteLong.x, alwaysContinue) - add(0x21, AND, DIRECT_X_INDIRECT, alwaysContinue) - add(0x23, AND, DIRECT_S, alwaysContinue) - add(0x25, AND, DIRECT, alwaysContinue) - add(0x27, AND, DIRECT_INDIRECT_LONG, alwaysContinue) - add(0x29, AND, IMMEDIATE_M, alwaysContinue) - add(0x2D, AND, ABSOLUTE, alwaysContinue) - add(0x2F, AND, ABSOLUTE_LONG, alwaysContinue) - add(0x31, AND, DIRECT_INDIRECT_Y, alwaysContinue) - add(0x32, AND, DIRECT_INDIRECT, alwaysContinue) - add(0x33, AND, DIRECT_S_INDIRECT_Y, alwaysContinue) - add(0x35, AND, DIRECT_X, alwaysContinue) - add(0x37, AND, DIRECT_INDIRECT_LONG_Y, alwaysContinue) - add(0x39, AND, ABSOLUTE_Y, alwaysContinue) - add(0x3D, AND, ABSOLUTE_X, alwaysContinue) - add(0x3F, AND, ABSOLUTE_LONG_X, alwaysContinue) - add(0x41, EOR, DIRECT_X_INDIRECT, alwaysContinue) - add(0x43, EOR, DIRECT_S, alwaysContinue) - add(0x45, EOR, DIRECT, alwaysContinue) - add(0x47, EOR, DIRECT_INDIRECT_LONG, alwaysContinue) - add(0x49, EOR, IMMEDIATE_M, alwaysContinue) - add(0x4D, EOR, ABSOLUTE, alwaysContinue) - add(0x4F, EOR, ABSOLUTE_LONG, alwaysContinue) - add(0x51, EOR, DIRECT_INDIRECT_Y, alwaysContinue) - add(0x52, EOR, DIRECT_INDIRECT, alwaysContinue) - add(0x53, EOR, DIRECT_S_INDIRECT_Y, alwaysContinue) - add(0x55, EOR, DIRECT_X, alwaysContinue) - add(0x57, EOR, DIRECT_INDIRECT_LONG_Y, alwaysContinue) - add(0x59, EOR, ABSOLUTE_Y, alwaysContinue) - add(0x5D, EOR, ABSOLUTE_X, alwaysContinue) - add(0x5F, EOR, ABSOLUTE_LONG_X, alwaysContinue) - add(0x01, ORA, DIRECT_X_INDIRECT, alwaysContinue) - add(0x03, ORA, DIRECT_S, alwaysContinue) - add(0x05, ORA, DIRECT, alwaysContinue) - add(0x07, ORA, DIRECT_INDIRECT_LONG, alwaysContinue) - add(0x09, ORA, IMMEDIATE_M, alwaysContinue) - add(0x0D, ORA, ABSOLUTE, alwaysContinue) - add(0x0F, ORA, ABSOLUTE_LONG, alwaysContinue) - add(0x11, ORA, DIRECT_INDIRECT_Y, alwaysContinue) - add(0x12, ORA, DIRECT_INDIRECT, alwaysContinue) - add(0x13, ORA, DIRECT_S_INDIRECT_Y, alwaysContinue) - add(0x15, ORA, DIRECT_X, alwaysContinue) - add(0x17, ORA, DIRECT_INDIRECT_LONG_Y, alwaysContinue) - add(0x19, ORA, ABSOLUTE_Y, alwaysContinue) - add(0x1D, ORA, ABSOLUTE_X, alwaysContinue) - add(0x1F, ORA, ABSOLUTE_LONG_X, alwaysContinue) + add(0x21, AND, Direct.x.indirect, alwaysContinue) + add(0x23, AND, Direct.s, alwaysContinue) + add(0x25, AND, Direct, alwaysContinue) + add(0x27, AND, Direct.indirectLong, alwaysContinue) + add(0x29, AND, ImmediateM, alwaysContinue) + add(0x2D, AND, Absolute, alwaysContinue) + add(0x2F, AND, AbsoluteLong, alwaysContinue) + add(0x31, AND, Direct.indirect.y, alwaysContinue) + add(0x32, AND, Direct.indirect, alwaysContinue) + add(0x33, AND, Direct.s.indirect.y, alwaysContinue) + add(0x35, AND, Direct.x, alwaysContinue) + add(0x37, AND, Direct.indirectLong.y, alwaysContinue) + add(0x39, AND, Absolute.y, alwaysContinue) + add(0x3D, AND, Absolute.x, alwaysContinue) + add(0x3F, AND, AbsoluteLong.x, alwaysContinue) + add(0x41, EOR, Direct.x.indirect, alwaysContinue) + add(0x43, EOR, Direct.s, alwaysContinue) + add(0x45, EOR, Direct, alwaysContinue) + add(0x47, EOR, Direct.indirectLong, alwaysContinue) + add(0x49, EOR, ImmediateM, alwaysContinue) + add(0x4D, EOR, Absolute, alwaysContinue) + add(0x4F, EOR, AbsoluteLong, alwaysContinue) + add(0x51, EOR, Direct.indirect.y, alwaysContinue) + add(0x52, EOR, Direct.indirect, alwaysContinue) + add(0x53, EOR, Direct.s.indirect.y, alwaysContinue) + add(0x55, EOR, Direct.x, alwaysContinue) + add(0x57, EOR, Direct.indirectLong.y, alwaysContinue) + add(0x59, EOR, Absolute.y, alwaysContinue) + add(0x5D, EOR, Absolute.x, alwaysContinue) + add(0x5F, EOR, AbsoluteLong.x, alwaysContinue) + add(0x01, ORA, Direct.x.indirect, alwaysContinue) + add(0x03, ORA, Direct.s, alwaysContinue) + add(0x05, ORA, Direct, alwaysContinue) + add(0x07, ORA, Direct.indirectLong, alwaysContinue) + add(0x09, ORA, ImmediateM, alwaysContinue) + add(0x0D, ORA, Absolute, alwaysContinue) + add(0x0F, ORA, AbsoluteLong, alwaysContinue) + add(0x11, ORA, Direct.indirect.y, alwaysContinue) + add(0x12, ORA, Direct.indirect, alwaysContinue) + add(0x13, ORA, Direct.s.indirect.y, alwaysContinue) + add(0x15, ORA, Direct.x, alwaysContinue) + add(0x17, ORA, Direct.indirectLong.y, alwaysContinue) + add(0x19, ORA, Absolute.y, alwaysContinue) + add(0x1D, ORA, Absolute.x, alwaysContinue) + add(0x1F, ORA, AbsoluteLong.x, alwaysContinue) - add(0x14, TRB, DIRECT, alwaysContinue) - add(0x1C, TRB, ABSOLUTE, alwaysContinue) - add(0x04, TSB, DIRECT, alwaysContinue) - add(0x0C, TSB, ABSOLUTE, alwaysContinue) + add(0x14, TRB, Direct, alwaysContinue) + add(0x1C, TRB, Absolute, alwaysContinue) + add(0x04, TSB, Direct, alwaysContinue) + add(0x0C, TSB, Absolute, alwaysContinue) - add(0x24, BIT, DIRECT, alwaysContinue) - add(0x2C, BIT, ABSOLUTE, alwaysContinue) - add(0x34, BIT, DIRECT_X, alwaysContinue) - add(0x3C, BIT, ABSOLUTE_X, alwaysContinue) - add(0x89, BIT, IMMEDIATE_M, alwaysContinue) + add(0x24, BIT, Direct, alwaysContinue) + add(0x2C, BIT, Absolute, alwaysContinue) + add(0x34, BIT, Direct.x, alwaysContinue) + add(0x3C, BIT, Absolute.x, alwaysContinue) + add(0x89, BIT, ImmediateM, alwaysContinue) - add(0x54, MVN, BLOCK_MOVE, alwaysContinue) - add(0x44, MVP, BLOCK_MOVE, alwaysContinue) + add(0x54, MVN, BlockMove, alwaysContinue) + add(0x44, MVP, BlockMove, alwaysContinue) - add(0xF4, PEA, IMMEDIATE_16, alwaysContinue) - add(0xD4, PEI, DIRECT, alwaysContinue) - add(0x62, PER, RELATIVE_LONG, alwaysContinue) + add(0xF4, PEA, Immediate16, alwaysContinue) + add(0xD4, PEI, Direct, alwaysContinue) + add(0x62, PER, RelativeLong, alwaysContinue) OPCODES = Array(256) { ocs[it]!! } } diff --git a/src/main/java/com/smallhacker/disbrowser/asm/SnesMapper.kt b/src/main/java/com/smallhacker/disbrowser/asm/SnesMapper.kt index 67f08ba..b811695 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/SnesMapper.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/SnesMapper.kt @@ -19,8 +19,8 @@ abstract class SnesMapper: MemorySpace { return entry.space[offset] } - fun toCanonical(address: SnesAddress): SnesAddress? { - val entry = areas[address.value.toUInt()] ?: return null + fun toCanonical(address: SnesAddress): SnesAddress { + val entry = areas[address.value.toUInt()] ?: return address val offset = address.value - entry.start return entry.canonicalStart + offset.toInt() } diff --git a/src/main/java/com/smallhacker/disbrowser/asm/State.kt b/src/main/java/com/smallhacker/disbrowser/asm/State.kt index d714e96..5b3ac42 100644 --- a/src/main/java/com/smallhacker/disbrowser/asm/State.kt +++ b/src/main/java/com/smallhacker/disbrowser/asm/State.kt @@ -4,7 +4,7 @@ import com.smallhacker.disbrowser.ImmStack import com.smallhacker.disbrowser.immStack 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 = immStack()) { +data class State(val origin: Instruction? = null, val memory: SnesMapper, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack = immStack(), val metadata: Metadata) { val m: Boolean? get() = flags.getBoolean(0x20u) val x: Boolean? get() = flags.getBoolean(0x10u) 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 -> val ptr = (dp.toUInt24() shl 8) or (directPage.toUInt24()) - SnesAddress(ptr) + memory.toCanonical(SnesAddress(ptr)) } fun resolveAbsoluteData(absolute: UShort) = db?.let { db -> 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()) - return SnesAddress(ptr) + val address = SnesAddress(ptr) + return memory.toCanonical(address) } private fun stackToString(): String { diff --git a/src/main/java/com/smallhacker/disbrowser/resource/DisassemblyResource.kt b/src/main/java/com/smallhacker/disbrowser/resource/DisassemblyResource.kt index 90d901e..5ad6ac0 100644 --- a/src/main/java/com/smallhacker/disbrowser/resource/DisassemblyResource.kt +++ b/src/main/java/com/smallhacker/disbrowser/resource/DisassemblyResource.kt @@ -1,7 +1,6 @@ package com.smallhacker.disbrowser.resource -import com.smallhacker.disbrowser.HtmlNode -import com.smallhacker.disbrowser.Service +import com.smallhacker.disbrowser.* import com.smallhacker.disbrowser.asm.SnesAddress import com.smallhacker.disbrowser.asm.VagueNumber import java.nio.charset.StandardCharsets @@ -17,7 +16,21 @@ class DisassemblyResource { @GET @Produces(MediaType.TEXT_HTML) 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 @@ -39,14 +52,24 @@ class DisassemblyResource { private fun handle(runner: () -> HtmlNode?): Response { try { - val disassembly = runner() + val output = runner() + ?: return Response.status(404).build() - return if (disassembly == null) - Response.status(404).build() - else - Response.ok(disassembly.toString().toByteArray(StandardCharsets.UTF_8)) - .encoding("UTF-8") - .build() + val html = html { + head { + title { text("Disassembly Browser") } + link {}.attr("rel", "stylesheet").attr("href", "/resources/style.css") + meta {}.attr("charset", "UTF-8") + } + 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) { e.printStackTrace() throw e diff --git a/src/main/java/com/smallhacker/disbrowser/util/misc.kt b/src/main/java/com/smallhacker/disbrowser/util/misc.kt index f7e35bd..21c7695 100644 --- a/src/main/java/com/smallhacker/disbrowser/util/misc.kt +++ b/src/main/java/com/smallhacker/disbrowser/util/misc.kt @@ -25,6 +25,10 @@ fun toHex(value: UInt, bytes: UInt): String { 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 .asSequence() .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 List.asReverseSequence(): Sequence = - ((size - 1) downTo 0).asSequence().map { this[it] } \ No newline at end of file + ((size - 1) downTo 0).asSequence().map { this[it] } + +fun MutableMap.removeIf(condition: (K, V) -> Boolean) { + this.asSequence() + .filter { condition(it.key, it.value) } + .map { it.key } + .toList() + .forEach { remove(it) } +} \ No newline at end of file diff --git a/src/main/resources/public/style.css b/src/main/resources/public/style.css index b04ec89..cc3d044 100644 --- a/src/main/resources/public/style.css +++ b/src/main/resources/public/style.css @@ -107,4 +107,24 @@ tr.line-active { .opcode-info{ 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]" } \ No newline at end of file diff --git a/src/main/ts/main.ts b/src/main/ts/main.ts index db18ef0..8029345 100644 --- a/src/main/ts/main.ts +++ b/src/main/ts/main.ts @@ -67,10 +67,10 @@ window.addEventListener("hashchange", function () { highlight(fromHash()) }, false); -let comments = document.getElementsByClassName("field-editable"); -for (let i = 0; i < comments.length; i++) { - let comment = comments[i]; - comment.addEventListener("change", e => { +let editables = document.getElementsByClassName("field-editable"); +for (let i = 0; i < editables.length; i++) { + let editable = editables[i]; + editable.addEventListener("change", e => { let target = (e.target); let field = target.dataset.field || ""; let address = target.dataset.address; @@ -79,6 +79,30 @@ for (let i = 0; i < comments.length; i++) { xhr(`/rest/${address}/${field}`, "POST", value) .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 = (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; }); } \ No newline at end of file