More stuff

This commit is contained in:
Smallhacker 2019-01-11 21:16:50 -05:00
parent 9881bf9e0b
commit e6445575d7
15 changed files with 330 additions and 128 deletions

View File

@ -3,6 +3,7 @@
"008000" : {
"label" : "ResetVector"
},
"00800a" : { },
"00801b" : {
"comment" : "\\ Turn off emulation mode"
},

View File

@ -10,28 +10,27 @@ import kotlin.reflect.KMutableProperty1
private val RESET_VECTOR_LOCATION = address(0x00_FFFC)
object Service {
private val romName = "Zelda no Densetsu - Kamigami no Triforce (Japan)"
private const val romName = "Zelda no Densetsu - Kamigami no Triforce (Japan)"
private val romDir = Paths.get("""P:\Emulation\ROMs\SNES""")
private val metaDir = Paths.get("""P:\Programming\dis-browser""")
private val metaFile = jsonFile<Metadata>(metaDir.resolve("$romName.json"), true)
private val metadata by lazy { metaFile.load() }
private val romData = lazy {
private val snesMemory by lazy {
val path = romDir.resolve("$romName.sfc")
RomData.load(path)
SnesLoRom(loadRomData(path))
}
fun showDisassemblyFromReset(): HtmlNode? {
val resetVectorLocation = RESET_VECTOR_LOCATION
val initialAddress = SnesAddress(romData.value.getWord(resetVectorLocation.pc).toUInt24())
val resetVector = snesMemory.getWord(RESET_VECTOR_LOCATION)
val fullResetVector = resetVector!!.toUInt24()
val initialAddress = SnesAddress(fullResetVector)
val flags = VagueNumber(0x30u)
return showDisassembly(initialAddress, flags)
}
fun showDisassembly(initialAddress: SnesAddress, flags: VagueNumber): HtmlNode? {
val rom = romData.value
val initialState = State(data = rom, address = initialAddress, flags = flags)
val initialState = State(memory = snesMemory, address = initialAddress, flags = flags)
val disassembly = Disassembler.disassemble(initialState, metadata, false)
return print(disassembly, metadata)

View File

@ -12,7 +12,7 @@ interface CodeUnit {
val preState: State?
val postState: State?
val bytes: RomData
val bytes: ValidMemorySpace
val opcode: Opcode
val lengthSuffix: String?
@ -62,7 +62,7 @@ interface CodeUnit {
class DataBlock(
override val opcode: Opcode,
override val bytes: RomData,
override val bytes: ValidMemorySpace,
override val presentedAddress: SnesAddress,
override val relativeAddress: SnesAddress,
override val linkedState: State?
@ -77,7 +77,7 @@ class DataBlock(
override val lengthSuffix: String? = null
}
class Instruction(override val bytes: RomData, override val opcode: Opcode, override val preState: State) : CodeUnit {
class Instruction(override val bytes: ValidMemorySpace, override val opcode: Opcode, override val preState: State) : CodeUnit {
override val address: SnesAddress get() = preState.address
override val relativeAddress get() = address
override val presentedAddress get() = address
@ -115,18 +115,59 @@ class Instruction(override val bytes: RomData, override val opcode: Opcode, over
return null
}
return referencedAddress()
// return when (opcode.mode) {
// Mode.ABSOLUTE_CODE -> preState.resolveAbsoluteCode(word)
// Mode.ABSOLUTE_LONG -> SnesAddress(long)
// Mode.RELATIVE -> relativeAddress + 2 + signedByte.toInt()
// Mode.RELATIVE_LONG -> relativeAddress + 3 + signedWord.toInt()
// Mode.CODE_WORD -> preState.resolveAbsoluteCode(word)
// Mode.CODE_LONG -> SnesAddress(long)
// Mode.DATA_WORD -> preState.resolveAbsoluteData(word)
// Mode.DATA_LONG -> SnesAddress(long)
// else -> null
// }
}
private fun referencedAddress(): SnesAddress? {
return when (opcode.mode) {
Mode.ABSOLUTE -> relativeAddress.withinBank(word)
Mode.ABSOLUTE -> preState.resolveAbsoluteData(word)
Mode.ABSOLUTE_CODE -> preState.resolveAbsoluteCode(word)
Mode.ABSOLUTE_INDIRECT -> preState.resolveAbsoluteData(word)
Mode.ABSOLUTE_INDIRECT_LONG -> preState.resolveAbsoluteData(word)
Mode.ABSOLUTE_LONG -> SnesAddress(long)
Mode.ABSOLUTE_LONG_X -> SnesAddress(long)
Mode.ABSOLUTE_X -> preState.resolveAbsoluteData(word)
Mode.ABSOLUTE_X_INDIRECT -> preState.resolveAbsoluteData(word)
Mode.ABSOLUTE_Y -> preState.resolveAbsoluteData(word)
Mode.BLOCK_MOVE -> null
Mode.CODE_WORD -> preState.resolveAbsoluteCode(word)
Mode.CODE_LONG -> SnesAddress(long)
Mode.DATA_BYTE -> null
Mode.DATA_WORD -> preState.resolveAbsoluteData(word)
Mode.DATA_LONG -> SnesAddress(long)
Mode.DIRECT -> preState.resolveDirectPage(byte)
Mode.DIRECT_X -> preState.resolveDirectPage(byte)
Mode.DIRECT_Y -> preState.resolveDirectPage(byte)
Mode.DIRECT_S -> null
Mode.DIRECT_INDIRECT -> preState.resolveDirectPage(byte)
Mode.DIRECT_INDIRECT_Y -> preState.resolveDirectPage(byte)
Mode.DIRECT_X_INDIRECT -> preState.resolveDirectPage(byte)
Mode.DIRECT_S_INDIRECT_Y -> null
Mode.DIRECT_INDIRECT_LONG -> preState.resolveDirectPage(byte)
Mode.DIRECT_INDIRECT_LONG_Y -> preState.resolveDirectPage(byte)
Mode.IMMEDIATE_8 -> null
Mode.IMMEDIATE_16 -> null
Mode.IMMEDIATE_M -> null
Mode.IMMEDIATE_X -> null
Mode.IMPLIED -> null
Mode.RELATIVE -> relativeAddress + 2 + signedByte.toInt()
Mode.RELATIVE_LONG -> relativeAddress + 3 + signedWord.toInt()
Mode.DATA_WORD -> relativeAddress.withinBank(word)
Mode.DATA_LONG -> SnesAddress(long)
else -> null
}
}
override fun toString(): String {
return "$address ${bytesToString()} ${opcode.mnemonic.displayName} ${opcode.mode.print(this).padEnd(100, ' ')} ($preState -> $postState)"
}
}
}

View File

@ -0,0 +1,93 @@
package com.smallhacker.disbrowser.asm
import com.smallhacker.disbrowser.util.UInt24
import com.smallhacker.disbrowser.util.joinBytes
import com.smallhacker.disbrowser.util.joinNullableBytes
import com.smallhacker.disbrowser.util.toUInt24
import java.nio.file.Files
import java.nio.file.Path
interface MemorySpace {
val size: UInt
operator fun get(address: UInt): UByte?
}
interface ValidMemorySpace : MemorySpace {
override operator fun get(address: UInt): UByte
}
object EmptyMemorySpace : ValidMemorySpace {
override val size get() = 0u
override fun get(address: UInt) = throw IllegalStateException()
}
fun MemorySpace.asSequence(): Sequence<UByte?> = (0u until size).asSequence().map { this[it] }
fun MemorySpace.getWord(address: UInt): UShort? = joinNullableBytes(this[address], this[address + 1u])?.toUShort()
fun MemorySpace.getLong(address: UInt): UInt24? = joinNullableBytes(this[address], this[address + 1u], this[address + 2u])?.toUInt24()
fun MemorySpace.range(start: UInt, length: UInt): MemorySpace = MemoryRange(this, start, length)
fun MemorySpace.range(start: SnesAddress, length: UInt): MemorySpace = range(start.value.toUInt(), length)
fun MemorySpace.validate(): ValidMemorySpace? {
if (asSequence().any { it == null }) {
return null
}
return ValidatedMemorySpace(this)
}
fun ValidMemorySpace.asSequence(): Sequence<UByte> = (0u until size).asSequence().map { this[it] }
fun ValidMemorySpace.getWord(address: UInt): UShort = joinBytes(this[address], this[address + 1u]).toUShort()
fun ValidMemorySpace.getLong(address: UInt): UInt24 = joinBytes(this[address], this[address + 1u], this[address + 2u]).toUInt24()
fun ValidMemorySpace.range(start: UInt, length: UInt): ValidMemorySpace = MemoryRange(this, start, length).validate()!!
fun ValidMemorySpace.range(start: SnesAddress, length: UInt): ValidMemorySpace = range(start.value.toUInt(), length).validate()!!
fun loadRomData(path: Path): MemorySpace {
val bytes = Files.readAllBytes(path).toUByteArray()
return ArrayMemorySpace(bytes)
}
class ArrayMemorySpace(private val bytes: UByteArray) : MemorySpace {
override val size = bytes.size.toUInt()
override fun get(address: UInt): UByte? {
if (address >= size) {
return null
}
return bytes[address.toInt()]
}
}
class UnreadableMemory(override val size: UInt) : MemorySpace {
override fun get(address: UInt): UByte? = null
}
private class MemoryRange(private val parent: MemorySpace, private val start: UInt, override val size: UInt) : MemorySpace {
override fun get(address: UInt) = if (address < size) parent[start + address] else null
}
private class ValidatedMemorySpace(private val parent: MemorySpace) : ValidMemorySpace {
override val size get() = parent.size
override fun get(address: UInt) = parent[address]!!
}
private class ReindexedMemorySpace(
private val parent: MemorySpace,
override val size: UInt,
private val mapper: (UInt) -> UInt
) : MemorySpace {
override fun get(address: UInt): UByte? {
if (address >= size) {
return null
}
val mapped = mapper(address)
return parent[mapped]
}
}
fun MemorySpace.deinterleave(entries: UInt, vararg startOffsets: UInt): MemorySpace {
val fieldCount = startOffsets.size.toUInt()
return ReindexedMemorySpace(this, entries * fieldCount) {
val entry = it / fieldCount
val field = it.rem(fieldCount)
startOffsets[field.toInt()] + entry
}
}

View File

@ -1,8 +1,11 @@
package com.smallhacker.disbrowser.asm
import com.fasterxml.jackson.annotation.*
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
import com.smallhacker.disbrowser.util.joinBytes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.smallhacker.disbrowser.util.joinNullableBytes
import com.smallhacker.disbrowser.util.toUInt24
import java.util.*
@ -63,20 +66,20 @@ class JmpIndirectLongInterleavedTable @JsonCreator constructor(
) : InstructionFlag {
private val uEntries get() = entries.toUInt()
fun readTable(data: RomData): Sequence<SnesAddress> {
return (0u until uEntries)
fun readTable(data: MemorySpace): Sequence<SnesAddress?> {
return (0 until entries)
.asSequence()
.map { it + start.pc }
.map { pc -> joinBytes(data[pc], data[pc + uEntries], data[pc + uEntries + uEntries]).toUInt24() }
.map { SnesAddress(it) }
.map { start + it }
.map { address -> joinNullableBytes(data[address], data[address + entries], data[address + entries + entries])?.toUInt24() }
.map { pointer -> pointer?.let { SnesAddress(it) } }
}
fun generateCode(jumpInstruction: Instruction): Sequence<DataBlock> {
val table = jumpInstruction.preState.data.deinterleave(uEntries,
start.pc,
(start + entries).pc,
(start + (2u * uEntries).toInt()).pc
)
val table = jumpInstruction.preState.memory.deinterleave(uEntries,
start.value.toUInt(),
(start + entries).value.toUInt(),
(start + (2 * uEntries.toInt())).value.toUInt()
).validate() ?: return emptySequence()
return (0u until uEntries)
.asSequence()
@ -93,11 +96,6 @@ class JmpIndirectLongInterleavedTable @JsonCreator constructor(
.mutateAddress { SnesAddress(target) }
.withOrigin(jumpInstruction)
)
// Instruction(
// data.range((start.value + offset).toUInt(), 3u),
// Opcode.POINTER_LONG,
// preState.mutateAddress { start -> start + offset.toInt() }
// )
}
}
@ -108,13 +106,13 @@ class JslTableRoutine @JsonCreator constructor(
@field:JsonProperty @JsonProperty private val entries: Int
) : InstructionFlag {
fun readTable(postJsr: State): Sequence<SnesAddress> {
val data = postJsr.data
return (0u until entries.toUInt())
fun readTable(postJsr: State): Sequence<SnesAddress?> {
val data = postJsr.memory
return (0 until entries)
.asSequence()
.map { postJsr.address.pc + (it * 3u) }
.map { pc -> joinBytes(data[pc], data[pc + 1u], data[pc + 2u]).toUInt24() }
.map { SnesAddress(it) }
.map { postJsr.address + (it * 3) }
.map { address -> joinNullableBytes(data[address], data[address + 1], data[address + 2])?.toUInt24() }
.map { pointer -> pointer?.let { SnesAddress(it) } }
}
override fun toString() = "JslTableRoutine($entries)"

View File

@ -11,7 +11,8 @@ enum class Mnemonic(private val nameOverride: String? = null, val alternativeNam
TCS, TDC, TRB, TSB, TSC, TSX, TXA, TXS, TXY, TYA, TYX,
WAI, WDM, XBA, XCE,
DB(nameOverride = ".db"), DW(nameOverride = ".dw"), DL(nameOverride = ".dl");
DB(nameOverride = ".db"), DW(nameOverride = ".dw"), DL(nameOverride = ".dl"),
UNKNOWN(nameOverride = "???");
val displayName get() = nameOverride ?: name
}

View File

@ -15,11 +15,14 @@ enum class Mode {
DATA_BYTE("$00", dataMode = true, showLengthSuffix = false),
DATA_WORD("$0000", dataMode = true, showLengthSuffix = false),
DATA_LONG("$000000", dataMode = true, showLengthSuffix = false),
CODE_WORD("$0000", dataMode = true, showLengthSuffix = false),
CODE_LONG("$000000", dataMode = true, showLengthSuffix = false),
IMPLIED("", showLengthSuffix = false),
IMMEDIATE_8("#$00", showLengthSuffix = false),
IMMEDIATE_16("#$0000", showLengthSuffix = false),
ABSOLUTE("$0000"),
ABSOLUTE_CODE("$0000"),
ABSOLUTE_X("$0000,x"),
ABSOLUTE_Y("$0000,y"),
ABSOLUTE_LONG("$000000"),

View File

@ -4,7 +4,6 @@ import java.util.HashMap
import com.smallhacker.disbrowser.asm.Mnemonic.*
import com.smallhacker.disbrowser.asm.Mode.*
import com.smallhacker.disbrowser.util.OldUByte
typealias SegmentEnder = Instruction.() -> SegmentEnd?
@ -51,8 +50,10 @@ class Opcode private constructor(val mnemonic: Mnemonic, val mode: Mode, val end
val DATA_WORD = Opcode(Mnemonic.DW, Mode.DATA_WORD, { null }, { it.preState })
val DATA_LONG = Opcode(Mnemonic.DL, Mode.DATA_LONG, { null }, { it.preState })
val POINTER_WORD = Opcode(Mnemonic.DW, Mode.DATA_WORD, { null }, { it.preState }).linking()
val POINTER_LONG = Opcode(Mnemonic.DL, Mode.DATA_LONG, { null }, { it.preState }).linking()
val POINTER_WORD = Opcode(Mnemonic.DW, Mode.CODE_WORD, { null }, { it.preState }).linking()
val POINTER_LONG = Opcode(Mnemonic.DL, Mode.CODE_LONG, { null }, { it.preState }).linking()
val UNKNOWN_OPCODE: Opcode
private val OPCODES: Array<Opcode>
@ -79,6 +80,7 @@ class Opcode private constructor(val mnemonic: Mnemonic, val mode: Mode, val end
val dynamicSubJumping: SegmentEnder = { stoppingSegmentEnd(address) }
val returning: SegmentEnder = { returnSegmentEnd(address) }
UNKNOWN_OPCODE = Opcode(UNKNOWN, IMPLIED, alwaysStop, Instruction::preState).stop()
add(0x00, BRK, IMMEDIATE_8, alwaysStop).stop()
add(0x02, COP, IMMEDIATE_8, alwaysStop).stop()
@ -100,14 +102,14 @@ class Opcode private constructor(val mnemonic: Mnemonic, val mode: Mode, val end
add(0xF0, BEQ, RELATIVE, branching).branching()
add(0x82, BRL, RELATIVE_LONG, alwaysBranching).stop().branching()
add(0x4C, JMP, ABSOLUTE, jumping).linking().stop()
add(0x4C, JMP, ABSOLUTE_CODE, jumping).linking().stop()
add(0x5C, JML, ABSOLUTE_LONG, jumping).linking().stop()
add(0x6C, JMP, ABSOLUTE_INDIRECT, dynamicJumping).stop()
add(0x7C, JMP, ABSOLUTE_X_INDIRECT, dynamicJumping).stop()
add(0xDC, JMP, ABSOLUTE_INDIRECT_LONG, dynamicJumping).stop()
add(0x22, JSL, ABSOLUTE_LONG, subJumping).linking().mayStop()
add(0x20, JSR, ABSOLUTE, subJumping).linking().mayStop()
add(0x20, JSR, ABSOLUTE_CODE, subJumping).linking().mayStop()
add(0xFC, JSR, ABSOLUTE_X_INDIRECT, dynamicSubJumping).mayStop()
add(0x60, RTS, IMPLIED, returning).stop()

View File

@ -1,66 +0,0 @@
package com.smallhacker.disbrowser.asm
import com.smallhacker.disbrowser.util.*
import java.nio.file.Files
import java.nio.file.Path
interface RomData {
val size: UInt
operator fun get(address: UInt): UByte
fun range(start: UInt, length: UInt): RomData = RomRange(this, start, length)
fun asSequence(): Sequence<UByte> = (0u until size).asSequence().map { this[it] }
companion object {
fun load(path: Path): RomData = ArrayRomData(Files.readAllBytes(path).toUByteArray())
}
}
fun romData(vararg bytes: UByte): RomData = ArrayRomData(bytes)
fun RomData.getWord(address: UInt) = joinBytes(this[address], this[address + 1u]).toUShort()
fun RomData.getLong(address: UInt) = joinBytes(this[address], this[address + 1u], this[address + 2u]).toUInt24()
private class ArrayRomData(private val bytes: UByteArray) : RomData {
override val size = bytes.size.toUInt()
override fun get(address: UInt) = rangeChecked(address) {
bytes[address.toInt()]
}
}
private class RomRange(private val parent: RomData, private val start: UInt, override val size: UInt) : RomData {
override fun get(address: UInt) = rangeChecked(address) {
parent[start + address]
}
}
private fun <T> RomData.rangeChecked(address: UInt, action: () -> T): T {
if (address >= size) {
throw IndexOutOfBoundsException("Index $address out of range: [0, $size)")
}
return action()
}
private class ReindexedRomData(
private val parent: RomData,
override val size: UInt,
private val mapper: (UInt) -> UInt
) : RomData {
override fun get(address: UInt) = rangeChecked(address) {
val mapped = mapper(address)
parent[mapped]
}
}
fun RomData.deinterleave(entries: UInt, vararg startOffsets: UInt): RomData {
val fieldCount = startOffsets.size.toUInt()
return ReindexedRomData(this, entries * fieldCount) {
val entry = it / fieldCount
val field = it.rem(fieldCount)
startOffsets[field.toInt()] + entry
}
}

View File

@ -7,8 +7,6 @@ import com.smallhacker.disbrowser.util.toUInt24
import com.smallhacker.disbrowser.util.tryParseInt
data class SnesAddress(val value: UInt24) : Comparable<SnesAddress> {
val pc = snesToPc(value)
operator fun plus(offset: Int) = SnesAddress(value + offset)
operator fun minus(offset: Int) = SnesAddress(value - offset)
operator fun inc() = plus(1)
@ -33,10 +31,4 @@ data class SnesAddress(val value: UInt24) : Comparable<SnesAddress> {
}
}
fun address(snesAddress: Int) = SnesAddress(snesAddress.toUInt24())
private fun snesToPc(value: UInt24): UInt {
// TODO: This is incredibly oversimplified. Anything that isn't a small LoROM will crash and burn
val intVal = value.toUInt()
return (intVal and 0x7FFFu) or ((intVal and 0x7F_0000u) shr 1)
}
fun address(snesAddress: Int) = SnesAddress(snesAddress.toUInt24())

View File

@ -0,0 +1,86 @@
package com.smallhacker.disbrowser.asm
import com.smallhacker.disbrowser.datatype.MutableRangeMap
import com.smallhacker.disbrowser.datatype.NaiveRangeMap
import com.smallhacker.disbrowser.util.toUInt24
abstract class SnesMapper: MemorySpace {
override val size = 0x100_0000u
private val areas: MutableRangeMap<UInt, UIntRange, MapperEntry> = NaiveRangeMap()
protected fun add(start: UInt, canonicalStart: UInt, memorySpace: MemorySpace) {
val range = start until (start + memorySpace.size)
areas[range] = MapperEntry(start, SnesAddress(canonicalStart.toUInt24()), memorySpace)
}
override fun get(address: UInt): UByte? {
val entry = areas[address] ?: return null
val offset = address - entry.start
return entry.space[offset]
}
fun toCanonical(address: SnesAddress): SnesAddress? {
val entry = areas[address.value.toUInt()] ?: return null
val offset = address.value - entry.start
return entry.canonicalStart + offset.toInt()
}
}
operator fun MemorySpace.get(address: SnesAddress) = get(address.value.toUInt())
fun MemorySpace.getWord(address: SnesAddress) = getWord(address.value.toUInt())
fun MemorySpace.getLong(address: SnesAddress) = getLong(address.value.toUInt())
data class MapperEntry(val start: UInt, val canonicalStart: SnesAddress, val space: MemorySpace)
class SnesLoRom(romData: MemorySpace): SnesMapper() {
init {
val ram = UnreadableMemory(0x2_0000u)
val ramMirror = ram.range(0x00_0000u, 0x00_2000u)
val registers = UnreadableMemory(0x6_0000u)
val sram = UnreadableMemory(0x0_8000u)
val ramStart = 0x7e_0000u
val regStart = 0x00_2000u
val srmStart = 0x70_0000u
var pc = 0x00_0000u
val high = 0x80_0000u
for (bank in 0x00u..0x3Fu) {
val ramArea = (bank shl 16)
val regArea = (bank shl 16) or 0x00_2000u
val romArea = (bank shl 16) or 0x00_8000u
add(ramArea, ramStart, ramMirror)
add(regArea, regStart, registers)
add(romArea, romArea, romData.range(pc, 0x00_8000u))
add(ramArea + high, ramStart, ramMirror)
add(regArea + high, regStart, registers)
add(romArea + high, romArea, romData.range(pc, 0x00_8000u))
pc += 0x00_8000u
}
for (bank in 0x40u..0x6Fu) {
val romArea = (bank shl 16) or 0x00_8000u
// Lower half unmapped
add(romArea, romArea, romData.range(pc, 0x00_8000u))
add(romArea + high, romArea, romData.range(pc, 0x00_8000u))
pc += 0x00_8000u
}
for (bank in 0x70u..0x7Du) {
val srmArea = (bank shl 16)
val romArea = (bank shl 16) or 0x00_8000u
add(srmArea, srmStart, sram)
add(srmArea + high, srmStart, sram)
add(romArea, romArea, romData.range(pc, 0x00_8000u))
add(romArea + high, romArea, romData.range(pc, 0x00_8000u))
pc += 0x00_8000u
}
for (bank in 0xFEu..0xFFu) {
val romArea = (bank shl 16) or 0x00_8000u
add(romArea, romArea, romData.range(pc, 0x00_8000u))
pc += 0x00_8000u
}
add(ramStart, ramStart, ram)
}
}

View File

@ -4,7 +4,7 @@ import com.smallhacker.disbrowser.ImmStack
import com.smallhacker.disbrowser.immStack
import com.smallhacker.disbrowser.util.toUInt24
data class State(val origin: Instruction? = null, val data: RomData, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack()) {
data class State(val origin: Instruction? = null, val memory: SnesMapper, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack()) {
val m: Boolean? get() = flags.getBoolean(0x20u)
val x: Boolean? get() = flags.getBoolean(0x10u)
val db: UByte? get() = pb // TODO
@ -56,16 +56,21 @@ data class State(val origin: Instruction? = null, val data: RomData, val address
return "A:${printSize(m)} XY:${printSize(x)} S:" + stackToString()
}
fun resolve(directPage: UByte) = dp?.let { dp ->
fun resolveDirectPage(directPage: UByte) = dp?.let { dp ->
val ptr = (dp.toUInt24() shl 8) or (directPage.toUInt24())
SnesAddress(ptr)
}
fun resolve(absolute: UShort)= db?.let { db ->
fun resolveAbsoluteData(absolute: UShort) = db?.let { db ->
val ptr = (db.toUInt24() shl 16) or (absolute.toUInt24())
SnesAddress(ptr)
}
fun resolveAbsoluteCode(absolute: UShort): SnesAddress {
val ptr = (pb.toUInt24() shl 16) or (absolute.toUInt24())
return SnesAddress(ptr)
}
private fun stackToString(): String {
return stack.reversed().asSequence()
.map { stackByteToString(it) }

View File

@ -0,0 +1,27 @@
package com.smallhacker.disbrowser.datatype
import com.smallhacker.disbrowser.util.asReverseSequence
interface RangeMap<K : Comparable<K>, R : ClosedRange<K>, V : Any> {
operator fun get(key: K): V?
}
interface MutableRangeMap<K : Comparable<K>, R : ClosedRange<K>, V: Any> : RangeMap<K, R, V> {
operator fun set(keyRange: R, value: V): RangeMap<K, R, V>
}
class NaiveRangeMap<K : Comparable<K>, R : ClosedRange<K>, V: Any> : MutableRangeMap<K, R, V> {
private val entries = ArrayList<Pair<R, V>>()
override fun get(key: K) = entries
.asReverseSequence()
.filter { it.first.contains(key) }
.map { it.second }
.firstOrNull()
override fun set(keyRange: R, value: V): RangeMap<K, R, V> {
entries += keyRange to value
return this
}
}

View File

@ -29,7 +29,8 @@ object Disassembler {
metadata[ins.address]?.flags?.forEach { flag ->
if (flag is JmpIndirectLongInterleavedTable) {
if (global) {
flag.readTable(state.data)
flag.readTable(state.memory)
.filterNotNull()
.map { ins.postState.copy(address = it) }
.forEach { tryAdd(it) }
}
@ -41,6 +42,7 @@ object Disassembler {
} else if (flag is JslTableRoutine) {
if (global) {
flag.readTable(ins.postState)
.filterNotNull()
.map { ins.postState.copy(address = it) }
.forEach { tryAdd(it) }
}
@ -162,10 +164,14 @@ object Disassembler {
}
private fun disassembleInstruction(state: State): Instruction {
val pc = state.address.pc
val opcode = Opcode.opcode(state.data[pc])
val opcodeValue = state.memory[state.address] ?: return unreadableInstruction(state)
val opcode = Opcode.opcode(opcodeValue)
val length = opcode.mode.instructionLength(state) ?: 1u
val bytes = state.data.range(pc, length)
val bytes = state.memory.range(state.address.value.toUInt(), length).validate()
?: return unreadableInstruction(state)
return Instruction(bytes, opcode, state)
}
private fun unreadableInstruction(state: State) =
Instruction(EmptyMemorySpace, Opcode.UNKNOWN_OPCODE, state)
}

View File

@ -30,6 +30,17 @@ fun joinBytes(vararg bytes: UByte) = bytes
.mapIndexed { index, v -> v.toUInt() shl (index * 8) }
.reduce { a, b -> a or b }
fun joinNullableBytes(vararg bytes: UByte?): UInt? {
if (bytes.any { it == null }) {
return null
}
return bytes
.asSequence()
.mapIndexed { index, v -> v!!.toUInt() shl (index * 8) }
.reduce { a, b -> a or b }
}
inline class UInt24(private val data: UInt) {
fun toUInt() = data and 0x00FF_FFFFu
fun toUShort() = toUInt().toUShort()
@ -43,7 +54,7 @@ inline class UInt24(private val data: UInt) {
infix fun or(v: UInt24) = (data or v.data).toUInt24()
infix fun or(v: UInt) = (data or v).toUInt24()
infix fun shl(v: Int) = (data shl v).toUInt24()
infix fun shr(v: Int)= (toUInt() shr v).toUInt24()
infix fun shr(v: Int) = (toUInt() shr v).toUInt24()
operator fun plus(v: UInt24) = (toUInt() + v.toUInt()).toUInt24()
operator fun plus(v: UInt) = (toUInt() + v).toUInt24()
@ -61,3 +72,6 @@ fun UByte.toUInt24() = this.toUInt().toUInt24()
fun Int.toUInt24() = this.toUInt().toUInt24()
fun Short.toUInt24() = this.toUInt().toUInt24()
fun Byte.toUInt24() = this.toUInt().toUInt24()
fun <T> List<T>.asReverseSequence(): Sequence<T> =
((size - 1) downTo 0).asSequence().map { this[it] }