mirror of
https://github.com/Smallhacker/disbrowser.git
synced 2025-04-06 16:37:30 +00:00
Stuff
This commit is contained in:
parent
32f3cdabff
commit
9881bf9e0b
@ -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
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -9,13 +9,13 @@ class Grid {
|
||||
|
||||
private val content = HashMap<Pair<Int, Int>, HtmlNode?>()
|
||||
private val cellClasses = HashMap<Pair<Int, Int>, String>()
|
||||
private val addresses = HashMap<Address, Int>()
|
||||
private val addresses = HashMap<SnesAddress, Int>()
|
||||
private val rowClasses = HashMap<Int, String>()
|
||||
private val rowId = HashMap<Int, String>()
|
||||
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?,
|
||||
|
@ -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)
|
||||
|
@ -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<MetadataLine, String?>, value: String) {
|
||||
fun updateMetadata(address: SnesAddress, field: KMutableProperty1<MetadataLine, String?>, 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<MetadataLine, String?>, value: String?) {
|
||||
private fun doUpdateMetadata(address: SnesAddress, field: KMutableProperty1<MetadataLine, String?>, value: String?) {
|
||||
val line = metadata.getOrCreate(address)
|
||||
field.set(line, value)
|
||||
|
||||
|
@ -3,7 +3,7 @@ package com.smallhacker.disbrowser.asm
|
||||
class Disassembly(lines: List<CodeUnit>) : Iterable<CodeUnit> {
|
||||
override fun iterator() = lineList.iterator() as Iterator<CodeUnit>
|
||||
|
||||
private val knownAddresses = HashSet<Address>()
|
||||
private val knownAddresses = HashSet<SnesAddress>()
|
||||
private val lineList = ArrayList<CodeUnit>()
|
||||
|
||||
init {
|
||||
@ -16,5 +16,5 @@ class Disassembly(lines: List<CodeUnit>) : Iterable<CodeUnit> {
|
||||
}
|
||||
}
|
||||
|
||||
operator fun contains(address: Address) = address in knownAddresses
|
||||
operator fun contains(address: SnesAddress) = address in knownAddresses
|
||||
}
|
@ -3,10 +3,10 @@ package com.smallhacker.disbrowser.asm
|
||||
import com.smallhacker.disbrowser.util.*
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -7,36 +7,34 @@ import com.smallhacker.disbrowser.util.toUInt24
|
||||
import java.util.*
|
||||
|
||||
class Metadata {
|
||||
private val lines: MutableMap<Address, MetadataLine>
|
||||
@JsonProperty
|
||||
private val code: TreeMap<SnesAddress, MetadataLine>
|
||||
|
||||
constructor() {
|
||||
this.lines = HashMap()
|
||||
this.code = TreeMap()
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
private constructor(@JsonProperty lines: Map<Address, MetadataLine>) {
|
||||
this.lines = HashMap(lines)
|
||||
private constructor(@JsonProperty code: Map<SnesAddress, MetadataLine>) {
|
||||
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<Address> {
|
||||
fun readTable(data: RomData): Sequence<SnesAddress> {
|
||||
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<DataBlock> {
|
||||
@ -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<Address> {
|
||||
fun readTable(postJsr: State): Sequence<SnesAddress> {
|
||||
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)"
|
||||
|
@ -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<InstructionFlag> = ArrayList()
|
||||
) {
|
||||
@JsonIgnore
|
||||
fun isEmpty() = (label == null) && (comment == null) && (preComment == null) && (flags.isEmpty())
|
||||
fun isEmpty() = (label == null) && (comment == null) && (preComment == null) && (length == 0) && (flags.isEmpty())
|
||||
}
|
||||
|
13
src/main/java/com/smallhacker/disbrowser/asm/MetadataRam.kt
Normal file
13
src/main/java/com/smallhacker/disbrowser/asm/MetadataRam.kt
Normal file
@ -0,0 +1,13 @@
|
||||
package com.smallhacker.disbrowser.asm
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||
data class MetadataRam(
|
||||
var label: String? = null,
|
||||
var length: Int?
|
||||
) {
|
||||
@JsonIgnore
|
||||
fun isEmpty() = (label == null) && (length == null)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.smallhacker.disbrowser.asm
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -1,25 +1,25 @@
|
||||
package com.smallhacker.disbrowser.asm
|
||||
|
||||
class Segment (val start: Address, val end: SegmentEnd, val instructions: List<Instruction>)
|
||||
class Segment (val start: SnesAddress, val end: SegmentEnd, val instructions: List<Instruction>)
|
||||
|
||||
class SegmentEnd(val address: Address, val local: List<State> = emptyList(), val remote: List<State> = emptyList(), val returnAddress: Address? = null, val returning: Boolean = false)
|
||||
class SegmentEnd(val address: SnesAddress, val local: List<State> = emptyList(), val remote: List<State> = emptyList(), val returnAddress: SnesAddress? = null, val returning: Boolean = false)
|
||||
|
||||
fun stoppingSegmentEnd(address: Address)
|
||||
fun stoppingSegmentEnd(address: SnesAddress)
|
||||
= SegmentEnd(address)
|
||||
|
||||
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)
|
||||
|
@ -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<Address> {
|
||||
val rom = (value and 0x8000u).toUInt() == 0u
|
||||
data class SnesAddress(val value: UInt24) : Comparable<SnesAddress> {
|
||||
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<Address> {
|
||||
@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
|
@ -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<VagueNumber> = immStack()) {
|
||||
data class State(val origin: Instruction? = null, val data: RomData, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack()) {
|
||||
val m: Boolean? get() = flags.getBoolean(0x20u)
|
||||
val 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) }
|
||||
|
@ -6,7 +6,7 @@ import kotlin.collections.ArrayList
|
||||
|
||||
object Disassembler {
|
||||
fun disassemble(initialState: State, metadata: Metadata, global: Boolean): Disassembly {
|
||||
val seen = HashSet<Address>()
|
||||
val seen = HashSet<SnesAddress>()
|
||||
val queue = ArrayDeque<State>()
|
||||
|
||||
fun tryAdd(state: State) {
|
||||
@ -79,7 +79,7 @@ object Disassembler {
|
||||
}
|
||||
|
||||
fun disassembleSegments(initialState: State): List<Segment> {
|
||||
val mapping = HashMap<Address, Segment>()
|
||||
val mapping = HashMap<SnesAddress, Segment>()
|
||||
val queue = ArrayDeque<State>()
|
||||
|
||||
val segments = ArrayList<Segment>()
|
||||
@ -119,12 +119,12 @@ object Disassembler {
|
||||
return segments
|
||||
}
|
||||
|
||||
fun disassembleSegment(initialState: State, mapping: MutableMap<Address, Segment>): Segment {
|
||||
fun disassembleSegment(initialState: State, mapping: MutableMap<SnesAddress, Segment>): Segment {
|
||||
val instructions = ArrayList<Instruction>()
|
||||
var lastState = initialState
|
||||
|
||||
val queue = ArrayDeque<State>()
|
||||
val seen = HashSet<Address>()
|
||||
val seen = HashSet<SnesAddress>()
|
||||
|
||||
fun finalize(segment: Segment): Segment {
|
||||
instructions.forEach {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user