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

182 lines
5.4 KiB
Kotlin
Raw Normal View History

2019-01-07 18:19:37 +00:00
package com.smallhacker.disbrowser.disassembler
import com.smallhacker.disbrowser.asm.*
import com.smallhacker.disbrowser.game.GameData
import com.smallhacker.disbrowser.game.JmpIndirectLongInterleavedTable
import com.smallhacker.disbrowser.game.JslTableRoutine
import com.smallhacker.disbrowser.game.NonReturningRoutine
2019-01-07 18:19:37 +00:00
import java.util.*
import kotlin.collections.ArrayList
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-07 18:19:37 +00:00
val queue = ArrayDeque<State>()
fun tryAdd(state: State) {
if (seen.add(state.address)) {
queue.add(state)
}
}
tryAdd(initialState)
2019-01-11 03:19:08 +00:00
val instructions = ArrayList<CodeUnit>()
2019-01-07 18:19:37 +00:00
while (queue.isNotEmpty()) {
val state = queue.remove()
val ins = disassembleInstruction(state)
instructions.add(ins)
var stop = (ins.opcode.continuation == Continuation.NO) or
(ins.opcode.mode.instructionLength(state) == null)
gameData[ins.address]?.flags?.forEach { flag ->
2019-01-11 03:19:08 +00:00
if (flag is JmpIndirectLongInterleavedTable) {
2019-01-07 18:19:37 +00:00
if (global) {
2019-01-12 02:16:50 +00:00
flag.readTable(state.memory)
.filterNotNull()
2019-01-07 18:19:37 +00:00
.map { ins.postState.copy(address = it) }
.forEach { tryAdd(it) }
}
2019-01-11 03:19:08 +00:00
flag.generateCode(ins)
.forEach { instructions.add(it) }
2019-01-07 18:19:37 +00:00
stop = true
2019-01-11 03:19:08 +00:00
} else if (flag is JslTableRoutine) {
2019-01-07 18:19:37 +00:00
if (global) {
2019-01-11 03:19:08 +00:00
flag.readTable(ins.postState)
2019-01-12 02:16:50 +00:00
.filterNotNull()
2019-01-07 18:19:37 +00:00
.map { ins.postState.copy(address = it) }
.forEach { tryAdd(it) }
}
stop = true
}
}
val linkedState = ins.linkedState
if (linkedState != null) {
gameData[linkedState.address]?.flags?.forEach {
2019-01-07 18:19:37 +00:00
if (it === NonReturningRoutine) {
stop = true
println(ins.address.toFormattedString())
}
}
}
if (!stop) {
tryAdd(ins.postState)
}
if (linkedState != null) {
if (ins.opcode.branch || global) {
tryAdd(linkedState)
}
}
}
val instructionList = instructions
2019-01-11 03:19:08 +00:00
.sortedBy { it.presentedAddress }
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-07 18:19:37 +00:00
val queue = ArrayDeque<State>()
val segments = ArrayList<Segment>()
fun tryAdd(state: State) {
if (!mapping.containsKey(state.address)) {
queue.add(state)
}
}
tryAdd(initialState)
while (queue.isNotEmpty()) {
val state = queue.remove()
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 {
}
}
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
val queue = ArrayDeque<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()) {
val state = queue.remove()
val ins = disassembleInstruction(state)
instructions.add(ins)
println(ins)
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))
}
private fun disassembleInstruction(state: State): Instruction {
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()
?: return unreadableInstruction(state)
2019-01-07 18:19:37 +00:00
return Instruction(bytes, opcode, state)
}
2019-01-12 02:16:50 +00:00
private fun unreadableInstruction(state: State) =
Instruction(EmptyMemorySpace, Opcode.UNKNOWN_OPCODE, state)
2019-01-07 18:19:37 +00:00
}