Generalized JSL jump table
This commit is contained in:
parent
3d7dcc08db
commit
1e2cf6978d
|
@ -3,7 +3,6 @@ package com.smallhacker.disbrowser
|
||||||
import com.smallhacker.disbrowser.asm.*
|
import com.smallhacker.disbrowser.asm.*
|
||||||
import com.smallhacker.disbrowser.game.Game
|
import com.smallhacker.disbrowser.game.Game
|
||||||
import com.smallhacker.disbrowser.disassembler.Disassembler
|
import com.smallhacker.disbrowser.disassembler.Disassembler
|
||||||
import com.smallhacker.disbrowser.game.GameData
|
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.disbrowser.util.toUInt24
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
|
|
||||||
|
@ -34,14 +33,13 @@ object Service {
|
||||||
return showDisassembly(game, initialAddress, flags)
|
return showDisassembly(game, initialAddress, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showDisassembly(game: Game, initialAddress: SnesAddress, flags: VagueNumber): HtmlNode? {
|
fun showDisassembly(game: Game, initialAddress: SnesAddress, flags: VagueNumber, global: Boolean = false): HtmlNode? {
|
||||||
val initialState = State(memory = game.memory, address = initialAddress, flags = flags, gameData = game.gameData)
|
val initialState = State(memory = game.memory, address = initialAddress, flags = flags, gameData = game.gameData)
|
||||||
val disassembly = Disassembler.disassemble(initialState, game.gameData, false)
|
val disassembly = Disassembler.disassemble(initialState, game.gameData, global)
|
||||||
|
|
||||||
return print(disassembly, game)
|
return print(disassembly, game)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun print(disassembly: Disassembly, game: Game): HtmlNode {
|
private fun print(disassembly: Disassembly, game: Game): HtmlNode {
|
||||||
val grid = Grid()
|
val grid = Grid()
|
||||||
disassembly.forEach {
|
disassembly.forEach {
|
||||||
|
|
|
@ -62,7 +62,7 @@ object Disassembler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteFlags.forFlag<JslTableRoutine> {
|
remoteFlags.forFlag<DynamicJumpRoutine> {
|
||||||
ins.continuation = Continuation.STOP
|
ins.continuation = Continuation.STOP
|
||||||
|
|
||||||
if (pointerTableEntries != null) {
|
if (pointerTableEntries != null) {
|
||||||
|
@ -158,7 +158,7 @@ object Disassembler {
|
||||||
end.local.forEach { queue.add(it) }
|
end.local.forEach { queue.add(it) }
|
||||||
|
|
||||||
end.remote.forEach {
|
end.remote.forEach {
|
||||||
|
queue.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,12 +191,11 @@ object Disassembler {
|
||||||
|
|
||||||
val ins = disassembleInstruction(state)
|
val ins = disassembleInstruction(state)
|
||||||
instructions.add(ins)
|
instructions.add(ins)
|
||||||
println(ins)
|
//println(ins)
|
||||||
|
|
||||||
val segmentEnd = ins.opcode.ender(ins)
|
val segmentEnd = ins.opcode.ender(ins)
|
||||||
|
|
||||||
if (segmentEnd != null) {
|
if (segmentEnd != null) {
|
||||||
|
|
||||||
return finalize(Segment(initialState.address, segmentEnd, instructions))
|
return finalize(Segment(initialState.address, segmentEnd, instructions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ operator fun GameData.get(address: SnesAddress?) = if (address == null) null els
|
||||||
Type(value = NonReturningRoutine::class, name = "NonReturningRoutine"),
|
Type(value = NonReturningRoutine::class, name = "NonReturningRoutine"),
|
||||||
Type(value = JmpIndirectLongInterleavedTable::class, name = "JmpIndirectLongInterleavedTable"),
|
Type(value = JmpIndirectLongInterleavedTable::class, name = "JmpIndirectLongInterleavedTable"),
|
||||||
Type(value = JslTableRoutine::class, name = "JslTableRoutine"),
|
Type(value = JslTableRoutine::class, name = "JslTableRoutine"),
|
||||||
|
Type(value = JsrTableRoutine::class, name = "JsrTableRoutine"),
|
||||||
Type(value = PointerTableLength::class, name = "PointerTableLength")
|
Type(value = PointerTableLength::class, name = "PointerTableLength")
|
||||||
)
|
)
|
||||||
interface InstructionFlag
|
interface InstructionFlag
|
||||||
|
@ -130,16 +131,15 @@ class JmpIndirectLongInterleavedTable @JsonCreator constructor(
|
||||||
override fun toString() = "JmpIndirectLongInterleavedTable($start, $entries)"
|
override fun toString() = "JmpIndirectLongInterleavedTable($start, $entries)"
|
||||||
}
|
}
|
||||||
|
|
||||||
class JslTableRoutine : InstructionFlag {
|
interface DynamicJumpRoutine : InstructionFlag {
|
||||||
fun readTable(postJsr: State, entryCount: Int): Sequence<SnesAddress?> {
|
fun readTable(postJump: State, entryCount: Int): Sequence<SnesAddress?> {
|
||||||
val data = postJsr.memory
|
|
||||||
return (0 until entryCount)
|
return (0 until entryCount)
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.map { postJsr.address + (it * 3) }
|
.map { readEntry(postJump, it) }
|
||||||
.map { address -> joinNullableBytes(data[address], data[address + 1], data[address + 2])?.toUInt24() }
|
|
||||||
.map { pointer -> pointer?.let { SnesAddress(it) } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun readEntry(postJump: State, index: Int): SnesAddress?
|
||||||
|
|
||||||
fun generatePointerTable(jumpInstruction: Instruction, entryCount: UInt?): Sequence<DataBlock> {
|
fun generatePointerTable(jumpInstruction: Instruction, entryCount: UInt?): Sequence<DataBlock> {
|
||||||
val count: UInt
|
val count: UInt
|
||||||
var certainty: Certainty
|
var certainty: Certainty
|
||||||
|
@ -155,41 +155,93 @@ class JslTableRoutine : InstructionFlag {
|
||||||
certaintyDecrease = 0
|
certaintyDecrease = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val start = jumpInstruction.postState.address
|
return (0u until count)
|
||||||
val memory = jumpInstruction.memory
|
|
||||||
|
|
||||||
return (0u until count.toUInt())
|
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.map { index -> index * 3u }
|
.mapNotNull { index ->
|
||||||
.mapNotNull { offset ->
|
generatePointer(jumpInstruction, index, certainty)
|
||||||
val pointerLoc= start + offset.toInt()
|
?.also {
|
||||||
val addressRange = memory.range(pointerLoc, 3u).validate()
|
certainty -= certaintyDecrease
|
||||||
|
}
|
||||||
if (addressRange == null) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
val target = addressRange.getLong(0u)
|
|
||||||
|
|
||||||
val block = DataBlock(
|
|
||||||
Opcode.CODE_POINTER_LONG,
|
|
||||||
addressRange,
|
|
||||||
pointerLoc,
|
|
||||||
jumpInstruction.relativeAddress,
|
|
||||||
jumpInstruction.opcode.mutate(jumpInstruction)
|
|
||||||
.mutateAddress { SnesAddress(target) }
|
|
||||||
.withOrigin(jumpInstruction),
|
|
||||||
memory,
|
|
||||||
certainty
|
|
||||||
)
|
|
||||||
certainty -= certaintyDecrease
|
|
||||||
block
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun generatePointer(jumpInstruction: Instruction, index: UInt, certainty: Certainty): DataBlock?
|
||||||
|
|
||||||
|
override fun toString(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class JslTableRoutine : DynamicJumpRoutine {
|
||||||
|
override fun readEntry(postJump: State, index: Int): SnesAddress? {
|
||||||
|
val data = postJump.memory
|
||||||
|
val address = postJump.address + (index * 3)
|
||||||
|
return joinNullableBytes(data[address], data[address + 1], data[address + 2])
|
||||||
|
?.toUInt24()
|
||||||
|
?.let { SnesAddress(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generatePointer(jumpInstruction: Instruction, index: UInt, certainty: Certainty): DataBlock? {
|
||||||
|
val offset = index * 3u
|
||||||
|
val pointerLoc = jumpInstruction.postState.address + offset.toInt()
|
||||||
|
val addressRange = jumpInstruction.memory.range(pointerLoc, 3u).validate()
|
||||||
|
|
||||||
|
if (addressRange == null) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
val target = addressRange.getLong(0u)
|
||||||
|
|
||||||
|
return DataBlock(
|
||||||
|
Opcode.CODE_POINTER_LONG,
|
||||||
|
addressRange,
|
||||||
|
pointerLoc,
|
||||||
|
jumpInstruction.relativeAddress,
|
||||||
|
jumpInstruction.opcode.mutate(jumpInstruction)
|
||||||
|
.mutateAddress { SnesAddress(target) }
|
||||||
|
.withOrigin(jumpInstruction),
|
||||||
|
jumpInstruction.memory,
|
||||||
|
certainty
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString() = "JslTableRoutine"
|
override fun toString() = "JslTableRoutine"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class JsrTableRoutine : DynamicJumpRoutine {
|
||||||
|
override fun readEntry(postJump: State, index: Int): SnesAddress? {
|
||||||
|
val data = postJump.memory
|
||||||
|
val address = postJump.address + (index * 2)
|
||||||
|
return joinNullableBytes(data[address], data[address + 1], postJump.pb)
|
||||||
|
?.toUInt24()
|
||||||
|
?.let { SnesAddress(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generatePointer(jumpInstruction: Instruction, index: UInt, certainty: Certainty): DataBlock? {
|
||||||
|
val offset = index * 2u
|
||||||
|
val pointerLoc = jumpInstruction.postState.address + offset.toInt()
|
||||||
|
val addressRange = jumpInstruction.memory.range(pointerLoc, 2u).validate()
|
||||||
|
|
||||||
|
if (addressRange == null) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
val target = addressRange.getWord(0u).toUInt24() or (jumpInstruction.postState.pb.toUInt24() shl 16)
|
||||||
|
|
||||||
|
return DataBlock(
|
||||||
|
Opcode.CODE_POINTER_WORD,
|
||||||
|
addressRange,
|
||||||
|
pointerLoc,
|
||||||
|
jumpInstruction.relativeAddress,
|
||||||
|
jumpInstruction.opcode.mutate(jumpInstruction)
|
||||||
|
.mutateAddress { SnesAddress(target) }
|
||||||
|
.withOrigin(jumpInstruction),
|
||||||
|
jumpInstruction.memory,
|
||||||
|
certainty
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString() = "JsrTableRoutine"
|
||||||
|
}
|
||||||
|
|
||||||
class PointerTableLength @JsonCreator constructor(
|
class PointerTableLength @JsonCreator constructor(
|
||||||
@field:JsonProperty @JsonProperty val entries: Int
|
@field:JsonProperty @JsonProperty val entries: Int
|
||||||
) : InstructionFlag {
|
) : InstructionFlag {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package com.smallhacker.disbrowser.resource
|
||||||
import com.smallhacker.disbrowser.*
|
import com.smallhacker.disbrowser.*
|
||||||
import com.smallhacker.disbrowser.asm.SnesAddress
|
import com.smallhacker.disbrowser.asm.SnesAddress
|
||||||
import com.smallhacker.disbrowser.asm.VagueNumber
|
import com.smallhacker.disbrowser.asm.VagueNumber
|
||||||
import com.smallhacker.disbrowser.game.GameSource
|
|
||||||
import com.smallhacker.disbrowser.game.getGameSource
|
import com.smallhacker.disbrowser.game.getGameSource
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import javax.ws.rs.GET
|
import javax.ws.rs.GET
|
||||||
|
@ -70,6 +69,32 @@ class DisassemblyResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("global/{address}")
|
||||||
|
@Produces(MediaType.TEXT_HTML)
|
||||||
|
fun getItGlobal(
|
||||||
|
@PathParam("game") gameName: String,
|
||||||
|
@PathParam("address") address: String
|
||||||
|
) = getItGlobal(gameName, address, "")
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("global/{address}/{state}")
|
||||||
|
@Produces(MediaType.TEXT_HTML)
|
||||||
|
fun getItGlobal(
|
||||||
|
@PathParam("game") gameName: String,
|
||||||
|
@PathParam("address") address: String,
|
||||||
|
@PathParam("state") state: String
|
||||||
|
): Response {
|
||||||
|
return handle {
|
||||||
|
games.getGame(gameName)?.let { game ->
|
||||||
|
SnesAddress.parse(address)?.let {
|
||||||
|
val flags = parseState(state)
|
||||||
|
Service.showDisassembly(game, it, flags, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handle(runner: () -> HtmlNode?): Response {
|
private fun handle(runner: () -> HtmlNode?): Response {
|
||||||
try {
|
try {
|
||||||
val output = runner()
|
val output = runner()
|
||||||
|
|
Loading…
Reference in New Issue