mirror of
https://github.com/Smallhacker/disbrowser.git
synced 2024-06-10 02:29:27 +00:00
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.game.Game
|
||||
import com.smallhacker.disbrowser.disassembler.Disassembler
|
||||
import com.smallhacker.disbrowser.game.GameData
|
||||
import com.smallhacker.disbrowser.util.toUInt24
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
|
||||
|
@ -34,14 +33,13 @@ object Service {
|
|||
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 disassembly = Disassembler.disassemble(initialState, game.gameData, false)
|
||||
val disassembly = Disassembler.disassemble(initialState, game.gameData, global)
|
||||
|
||||
return print(disassembly, game)
|
||||
}
|
||||
|
||||
|
||||
private fun print(disassembly: Disassembly, game: Game): HtmlNode {
|
||||
val grid = Grid()
|
||||
disassembly.forEach {
|
||||
|
|
|
@ -62,7 +62,7 @@ object Disassembler {
|
|||
}
|
||||
}
|
||||
|
||||
remoteFlags.forFlag<JslTableRoutine> {
|
||||
remoteFlags.forFlag<DynamicJumpRoutine> {
|
||||
ins.continuation = Continuation.STOP
|
||||
|
||||
if (pointerTableEntries != null) {
|
||||
|
@ -158,7 +158,7 @@ object Disassembler {
|
|||
end.local.forEach { queue.add(it) }
|
||||
|
||||
end.remote.forEach {
|
||||
|
||||
queue.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,12 +191,11 @@ object Disassembler {
|
|||
|
||||
val ins = disassembleInstruction(state)
|
||||
instructions.add(ins)
|
||||
println(ins)
|
||||
//println(ins)
|
||||
|
||||
val segmentEnd = ins.opcode.ender(ins)
|
||||
|
||||
if (segmentEnd != null) {
|
||||
|
||||
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 = JmpIndirectLongInterleavedTable::class, name = "JmpIndirectLongInterleavedTable"),
|
||||
Type(value = JslTableRoutine::class, name = "JslTableRoutine"),
|
||||
Type(value = JsrTableRoutine::class, name = "JsrTableRoutine"),
|
||||
Type(value = PointerTableLength::class, name = "PointerTableLength")
|
||||
)
|
||||
interface InstructionFlag
|
||||
|
@ -130,16 +131,15 @@ class JmpIndirectLongInterleavedTable @JsonCreator constructor(
|
|||
override fun toString() = "JmpIndirectLongInterleavedTable($start, $entries)"
|
||||
}
|
||||
|
||||
class JslTableRoutine : InstructionFlag {
|
||||
fun readTable(postJsr: State, entryCount: Int): Sequence<SnesAddress?> {
|
||||
val data = postJsr.memory
|
||||
interface DynamicJumpRoutine : InstructionFlag {
|
||||
fun readTable(postJump: State, entryCount: Int): Sequence<SnesAddress?> {
|
||||
return (0 until entryCount)
|
||||
.asSequence()
|
||||
.map { postJsr.address + (it * 3) }
|
||||
.map { address -> joinNullableBytes(data[address], data[address + 1], data[address + 2])?.toUInt24() }
|
||||
.map { pointer -> pointer?.let { SnesAddress(it) } }
|
||||
.map { readEntry(postJump, it) }
|
||||
}
|
||||
|
||||
fun readEntry(postJump: State, index: Int): SnesAddress?
|
||||
|
||||
fun generatePointerTable(jumpInstruction: Instruction, entryCount: UInt?): Sequence<DataBlock> {
|
||||
val count: UInt
|
||||
var certainty: Certainty
|
||||
|
@ -155,41 +155,93 @@ class JslTableRoutine : InstructionFlag {
|
|||
certaintyDecrease = 0
|
||||
}
|
||||
|
||||
val start = jumpInstruction.postState.address
|
||||
val memory = jumpInstruction.memory
|
||||
|
||||
return (0u until count.toUInt())
|
||||
return (0u until count)
|
||||
.asSequence()
|
||||
.map { index -> index * 3u }
|
||||
.mapNotNull { offset ->
|
||||
val pointerLoc= start + offset.toInt()
|
||||
val addressRange = memory.range(pointerLoc, 3u).validate()
|
||||
|
||||
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
|
||||
}
|
||||
.mapNotNull { index ->
|
||||
generatePointer(jumpInstruction, index, certainty)
|
||||
?.also {
|
||||
certainty -= certaintyDecrease
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
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(
|
||||
@field:JsonProperty @JsonProperty val entries: Int
|
||||
) : InstructionFlag {
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.smallhacker.disbrowser.resource
|
|||
import com.smallhacker.disbrowser.*
|
||||
import com.smallhacker.disbrowser.asm.SnesAddress
|
||||
import com.smallhacker.disbrowser.asm.VagueNumber
|
||||
import com.smallhacker.disbrowser.game.GameSource
|
||||
import com.smallhacker.disbrowser.game.getGameSource
|
||||
import java.nio.charset.StandardCharsets
|
||||
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 {
|
||||
try {
|
||||
val output = runner()
|
||||
|
|
Loading…
Reference in New Issue
Block a user