160 lines
5.0 KiB
Kotlin
160 lines
5.0 KiB
Kotlin
package com.smallhacker.disbrowser
|
|
|
|
import com.smallhacker.disbrowser.asm.*
|
|
import com.smallhacker.disbrowser.disassembler.Disassembler
|
|
import com.smallhacker.disbrowser.util.tryParseInt
|
|
import java.nio.file.Paths
|
|
import javax.ws.rs.GET
|
|
import javax.ws.rs.Path
|
|
import javax.ws.rs.PathParam
|
|
import javax.ws.rs.Produces
|
|
import javax.ws.rs.core.MediaType
|
|
import javax.ws.rs.core.Response
|
|
|
|
private val RESET_VECTOR_LOCATION = Address(0x00_FFFC)
|
|
|
|
@Path("/")
|
|
class MyResource {
|
|
private val romData = lazy {
|
|
//RomData.load(Paths.get("P:\\Emulation\\ROMs\\SNES\\Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"))
|
|
RomData.load(Paths.get("""P:\Emulation\ROMs\SNES\Super Mario World.sfc"""))
|
|
}
|
|
|
|
@GET
|
|
fun getIt(): Response {
|
|
return handle {
|
|
val resetVectorLocation = RESET_VECTOR_LOCATION
|
|
val initialAddress = Address(romData.value.getWord(resetVectorLocation.pc).value)
|
|
val flags = parseState("MX")
|
|
showDisassembly(initialAddress, flags)
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("{address}")
|
|
fun getIt(@PathParam("address") address: String): Response {
|
|
return handle {
|
|
parseAddress(address)?.let {
|
|
val flags = parseState("")
|
|
showDisassembly(it, flags)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@GET
|
|
@Path("{address}/{state}")
|
|
@Produces(MediaType.TEXT_HTML)
|
|
fun getIt(@PathParam("address") address: String, @PathParam("state") state: String): Response {
|
|
return handle {
|
|
parseAddress(address)?.let {
|
|
val flags = parseState(state)
|
|
showDisassembly(it, flags)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun parseAddress(address: String): Address? {
|
|
return tryParseInt(address, 16)
|
|
?.let { Address(it) }
|
|
}
|
|
|
|
private fun handle(runner: () -> HtmlNode?): Response {
|
|
try {
|
|
val disassembly = runner()
|
|
|
|
return if (disassembly == null)
|
|
Response.status(404).build()
|
|
else
|
|
Response.ok(disassembly.toString(), MediaType.TEXT_HTML).build()
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
throw e
|
|
}
|
|
}
|
|
|
|
private fun showDisassembly(initialAddress: Address, flags: VagueNumber): HtmlNode? {
|
|
val rom = romData.value
|
|
|
|
val metadata = getMetadata()
|
|
|
|
val initialState = State(data = rom, address = initialAddress, flags = flags)
|
|
val disassembly = Disassembler.disassemble(initialState, metadata, false)
|
|
|
|
return print(disassembly, metadata)
|
|
}
|
|
|
|
private fun parseState(state: String): VagueNumber {
|
|
var flags = VagueNumber()
|
|
flags = parseFlag(state, flags, 'M', 'm', 0x20)
|
|
flags = parseFlag(state, flags, 'X', 'x', 0x10)
|
|
return flags
|
|
}
|
|
|
|
@GET
|
|
@Path("resources/{file}.{ext}")
|
|
fun getCss(@PathParam("file") file: String, @PathParam("ext") ext: String): Response {
|
|
val mime = when (ext) {
|
|
"js" -> "application/javascript"
|
|
"css" -> "text/css"
|
|
else -> null
|
|
}
|
|
|
|
if (mime != null) {
|
|
javaClass.getResourceAsStream("/$file.$ext")
|
|
?.bufferedReader()
|
|
?.use {
|
|
return Response.ok(it.readText())
|
|
.type(mime)
|
|
.build()
|
|
}
|
|
}
|
|
|
|
return Response.status(404).build()
|
|
}
|
|
|
|
|
|
private fun parseFlag(state: String, flags: VagueNumber, set: Char, clear: Char, mask: Int): VagueNumber = when {
|
|
state.contains(set) -> flags.withBits(mask)
|
|
state.contains(clear) -> flags.withoutBits(mask)
|
|
else -> flags
|
|
}
|
|
|
|
private fun print(disassembly: Disassembly, metadata: Metadata): HtmlNode {
|
|
val grid = Grid()
|
|
disassembly.forEach { grid.add(it, metadata[it.address], disassembly) }
|
|
disassembly.asSequence()
|
|
.mapNotNull {
|
|
it.linkedState
|
|
?.let { link ->
|
|
it.address to link.address
|
|
}
|
|
}
|
|
.sortedBy { Math.abs(it.first.value - it.second.value) }
|
|
.forEach { grid.arrow(it.first, it.second) }
|
|
|
|
return html {
|
|
head {
|
|
title { text("Disassembly Browser") }
|
|
link {}.attr("rel", "stylesheet").attr("href", "/resources/style.css")
|
|
}
|
|
body {
|
|
grid.output().appendTo(parent)
|
|
script().attr("src", "/resources/main.js")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getMetadata(): Metadata {
|
|
return metadata {
|
|
at(0x00_8000) { label = "RESET_VECTOR" }
|
|
at(0x00_80c6) { flags.add(JmpIndirectLongInterleavedTable(Address(0x00_8061), 28)) }
|
|
at(0x00_879c) { flags.add(NonReturningRoutine) }
|
|
at(0x02_87d0) { flags.add(JslTableRoutine(4)) }
|
|
at(0x0c_c115) { flags.add(JslTableRoutine(12)) }
|
|
//at(0x0c_c115) { stop = true }
|
|
}
|
|
}
|
|
}
|
|
|