Generalized JSL jump table

This commit is contained in:
Smallhacker 2019-01-18 22:19:30 +01:00
parent 3d7dcc08db
commit 1e2cf6978d
4 changed files with 117 additions and 43 deletions

View File

@ -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 {

View File

@ -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))
}

View File

@ -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 {

View File

@ -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()