disbrowser/src/main/java/com/smallhacker/disbrowser/disassembler/Disassembler.kt

223 lines
7.2 KiB
Kotlin
Raw Normal View History

2019-01-07 18:19:37 +00:00
package com.smallhacker.disbrowser.disassembler
import com.smallhacker.disbrowser.asm.*
2019-01-15 05:54:54 +00:00
import com.smallhacker.disbrowser.game.*
2019-01-18 21:51:37 +00:00
import com.smallhacker.disbrowser.util.LifoQueue
2019-01-15 05:54:54 +00:00
import com.smallhacker.disbrowser.util.mutableMultiMap
import com.smallhacker.disbrowser.util.putSingle
2019-01-07 18:19:37 +00:00
import kotlin.collections.ArrayList
2019-01-15 05:54:54 +00:00
import kotlin.collections.HashMap
2019-01-07 18:19:37 +00:00
object Disassembler {
fun disassemble(initialState: State, gameData: GameData, global: Boolean): Disassembly {
2019-01-11 16:35:35 +00:00
val seen = HashSet<SnesAddress>()
2019-01-18 21:51:37 +00:00
val queue = LifoQueue<State>()
2019-01-15 05:54:54 +00:00
val origins = mutableMultiMap<SnesAddress, SnesAddress>()
val instructionMap = HashMap<SnesAddress, MutableInstruction>()
2019-01-07 18:19:37 +00:00
2019-01-15 05:54:54 +00:00
fun tryAdd(state: State, origin: SnesAddress?) {
if (origin != null) {
origins.putSingle(state.address, origin)
}
2019-01-07 18:19:37 +00:00
if (seen.add(state.address)) {
queue.add(state)
}
}
2019-01-15 05:54:54 +00:00
tryAdd(initialState, null)
2019-01-07 18:19:37 +00:00
2019-01-11 03:19:08 +00:00
val instructions = ArrayList<CodeUnit>()
2019-01-07 18:19:37 +00:00
while (queue.isNotEmpty()) {
2019-01-18 21:51:37 +00:00
val state = queue.removeNext()
2019-01-07 18:19:37 +00:00
val ins = disassembleInstruction(state)
instructions.add(ins)
2019-01-15 05:54:54 +00:00
instructionMap[ins.address] = ins
2019-01-07 18:19:37 +00:00
2019-01-15 05:54:54 +00:00
if (ins.opcode.mode.instructionLength(state) == null) {
ins.continuation = Continuation.INSUFFICIENT_DATA
}
2019-01-07 18:19:37 +00:00
2019-01-15 05:54:54 +00:00
val linkedState = ins.linkedState
val localAddress = ins.address
val remoteAddress = linkedState?.address
val localFlags = gameData.flagsAt(localAddress)
val remoteFlags = gameData.flagsAt(remoteAddress)
val pointerTableEntries = localFlags.findFlag<PointerTableLength>()?.entries
localFlags.forFlag<JmpIndirectLongInterleavedTable> {
ins.continuation = Continuation.STOP
if (global) {
readTable(state.memory)
.filterNotNull()
.map { ins.postState.copy(address = it) }
.forEach { tryAdd(it, ins.address) }
}
generatePointerTable(ins).forEach {
instructions.add(it)
}
}
2019-01-11 03:19:08 +00:00
2019-01-18 21:19:30 +00:00
remoteFlags.forFlag<DynamicJumpRoutine> {
2019-01-15 05:54:54 +00:00
ins.continuation = Continuation.STOP
2019-01-11 03:19:08 +00:00
2019-01-15 05:54:54 +00:00
if (pointerTableEntries != null) {
2019-01-07 18:19:37 +00:00
if (global) {
2019-01-15 05:54:54 +00:00
readTable(ins.postState, pointerTableEntries)
2019-01-12 02:16:50 +00:00
.filterNotNull()
2019-01-07 18:19:37 +00:00
.map { ins.postState.copy(address = it) }
2019-01-15 05:54:54 +00:00
.forEach { tryAdd(it, ins.address) }
2019-01-07 18:19:37 +00:00
}
}
2019-01-15 05:54:54 +00:00
generatePointerTable(ins, pointerTableEntries?.toUInt()).forEach {
instructions.add(it)
2019-01-07 18:19:37 +00:00
}
2019-01-15 05:54:54 +00:00
2019-01-07 18:19:37 +00:00
}
2019-01-15 05:54:54 +00:00
remoteFlags.forFlag<NonReturningRoutine> {
ins.continuation = Continuation.STOP
}
if (!ins.continuation.shouldStop) {
tryAdd(ins.postState, ins.address)
2019-01-07 18:19:37 +00:00
}
if (linkedState != null) {
if (ins.opcode.branch || global) {
2019-01-15 05:54:54 +00:00
tryAdd(linkedState, ins.address)
2019-01-07 18:19:37 +00:00
}
}
}
2019-01-15 05:54:54 +00:00
val fatalSeen = HashSet<SnesAddress>()
2019-01-18 21:51:37 +00:00
val fatalQueue = LifoQueue<SnesAddress>()
2019-01-15 05:54:54 +00:00
fun tryAddFatal(snesAddress: SnesAddress) {
if (fatalSeen.add(snesAddress)) {
2019-01-18 21:51:37 +00:00
fatalQueue.add(snesAddress)
2019-01-15 05:54:54 +00:00
}
}
instructions.asSequence()
.filterIsInstance<Instruction>()
.filter { it.continuation == Continuation.FATAL_ERROR }
.forEach { tryAddFatal(it.address) }
while (fatalQueue.isNotEmpty()) {
2019-01-18 21:51:37 +00:00
val badAddress = fatalQueue.removeNext()!!
2019-01-15 05:54:54 +00:00
val instruction = instructionMap[badAddress] ?: continue
val mnemonic = instruction.opcode.mnemonic
if (mnemonic == Mnemonic.JSL || mnemonic == Mnemonic.JSR) continue
instruction.certainty = Certainty.PROBABLY_WRONG
origins[badAddress]?.forEach{tryAddFatal(it)}
}
2019-01-07 18:19:37 +00:00
val instructionList = instructions
2019-01-14 05:23:19 +00:00
.sortedBy { it.sortedAddress }
2019-01-07 18:19:37 +00:00
.toList()
return Disassembly(instructionList)
}
fun disassembleSegments(initialState: State): List<Segment> {
2019-01-11 16:35:35 +00:00
val mapping = HashMap<SnesAddress, Segment>()
2019-01-18 21:51:37 +00:00
val queue = LifoQueue<State>()
2019-01-07 18:19:37 +00:00
val segments = ArrayList<Segment>()
fun tryAdd(state: State) {
if (!mapping.containsKey(state.address)) {
queue.add(state)
}
}
tryAdd(initialState)
while (queue.isNotEmpty()) {
2019-01-18 21:51:37 +00:00
val state = queue.removeNext()
2019-01-07 18:19:37 +00:00
if (mapping.containsKey(state.address)) {
continue
}
val segment = disassembleSegment(state, mapping)
if (segment.instructions.isEmpty()) {
continue
}
segments.add(segment)
val end = segment.end
end.local.forEach { queue.add(it) }
end.remote.forEach {
2019-01-18 21:19:30 +00:00
queue.add(it)
2019-01-07 18:19:37 +00:00
}
}
return segments
}
2019-01-11 16:35:35 +00:00
fun disassembleSegment(initialState: State, mapping: MutableMap<SnesAddress, Segment>): Segment {
2019-01-07 18:19:37 +00:00
val instructions = ArrayList<Instruction>()
var lastState = initialState
2019-01-18 21:51:37 +00:00
val queue = LifoQueue<State>()
2019-01-11 16:35:35 +00:00
val seen = HashSet<SnesAddress>()
2019-01-07 18:19:37 +00:00
fun finalize(segment: Segment): Segment {
instructions.forEach {
mapping[it.address] = segment
}
return segment
}
fun tryAdd(state: State) {
if (seen.add(state.address)) {
queue.add(state)
}
}
tryAdd(initialState)
while (queue.isNotEmpty()) {
2019-01-18 21:51:37 +00:00
val state = queue.removeNext()
2019-01-07 18:19:37 +00:00
val ins = disassembleInstruction(state)
instructions.add(ins)
2019-01-18 21:19:30 +00:00
//println(ins)
2019-01-07 18:19:37 +00:00
val segmentEnd = ins.opcode.ender(ins)
if (segmentEnd != null) {
return finalize(Segment(initialState.address, segmentEnd, instructions))
}
tryAdd(ins.postState)
lastState = ins.postState
}
return finalize(Segment(initialState.address, continuationSegmentEnd(lastState), instructions))
}
2019-01-15 05:54:54 +00:00
private fun disassembleInstruction(state: State): MutableInstruction {
2019-01-12 02:16:50 +00:00
val opcodeValue = state.memory[state.address] ?: return unreadableInstruction(state)
val opcode = Opcode.opcode(opcodeValue)
2019-01-11 03:19:08 +00:00
val length = opcode.mode.instructionLength(state) ?: 1u
2019-01-12 02:16:50 +00:00
val bytes = state.memory.range(state.address.value.toUInt(), length).validate()
2019-01-15 05:54:54 +00:00
?: return unreadableInstruction(state)
val continuation = opcode.continuation
val certainty = Certainty.PROBABLY_CORRECT
return MutableInstruction(bytes, opcode, state, continuation, certainty)
2019-01-07 18:19:37 +00:00
}
2019-01-12 02:16:50 +00:00
private fun unreadableInstruction(state: State) =
2019-01-15 05:54:54 +00:00
MutableInstruction(EmptyMemorySpace, Opcode.UNKNOWN_OPCODE, state, Continuation.INSUFFICIENT_DATA, Certainty.PROBABLY_WRONG)
2019-01-07 18:19:37 +00:00
}