package com.smallhacker.disbrowser.asm import com.smallhacker.disbrowser.game.GameData import com.smallhacker.disbrowser.game.get import com.smallhacker.disbrowser.memory.SnesAddress import com.smallhacker.disbrowser.memory.SnesMemory import com.smallhacker.disbrowser.memory.ValidMemorySpace import com.smallhacker.disbrowser.memory.asSequence import com.smallhacker.util.joinBytes import com.smallhacker.util.toHex import com.smallhacker.util.toUInt24 interface CodeUnit { val address: SnesAddress? val relativeAddress: SnesAddress val indicativeAddress: SnesAddress val sortedAddress: SnesAddress val nextSortedAddress: SnesAddress val linkedState: State? val preState: State? val postState: State? val bytes: ValidMemorySpace val opcode: Opcode val lengthSuffix: String? val memory: SnesMemory val certainty: Certainty fun operandByte(index: UInt): UByte = bytes[opcode.operandIndex + index] fun bytesToString(): String { return bytes.asSequence() .map { it.toHex() } .joinToString(" ") .padEnd(11, ' ') } val operandLength: UInt? val signedByte get() = byte.toByte() val signedWord get() = word.toShort() val byte get() = operandByte(0u) val byte2 get() = operandByte(1u) val word get() = joinBytes(operandByte(0u), operandByte(1u)).toUShort() val long get() = joinBytes(operandByte(0u), operandByte(1u), operandByte(2u)).toUInt24() val value get() = when (operandLength?.toInt()) { 0 -> 0u 1 -> byte.toUInt() 2 -> word.toUInt() 3 -> long.toUInt() else -> null } } class DataBlock( override val opcode: Opcode, override val bytes: ValidMemorySpace, override val address: SnesAddress?, override val indicativeAddress: SnesAddress, override val sortedAddress: SnesAddress, override val relativeAddress: SnesAddress, override val linkedState: State?, override val memory: SnesMemory, override val certainty: Certainty ) : CodeUnit { constructor( opcode: Opcode, bytes: ValidMemorySpace, indicativeAddress: SnesAddress, sortedAddress: SnesAddress, relativeAddress: SnesAddress, linkedState: State?, memory: SnesMemory, certainty: Certainty ) : this(opcode, bytes, null, indicativeAddress, sortedAddress, relativeAddress, linkedState, memory, certainty) constructor( opcode: Opcode, bytes: ValidMemorySpace, address: SnesAddress, relativeAddress: SnesAddress, linkedState: State?, memory: SnesMemory, certainty: Certainty ) : this(opcode, bytes, address, address, address, relativeAddress, linkedState, memory, certainty) override val nextSortedAddress: SnesAddress get() = sortedAddress + operandLength.toInt() override val operandLength get() = bytes.size override val preState: State? = null override val postState: State? = null override val lengthSuffix: String? = null } interface Instruction : CodeUnit { override val preState: State override val address: SnesAddress override val postState: State val continuation: Continuation val showLengthSuffix: Boolean fun link(): SnesAddress? fun referencedAddress(): SnesAddress? override fun toString(): String } class MutableInstruction( override val bytes: ValidMemorySpace, override val opcode: Opcode, override val preState: State, override var continuation: Continuation, override var certainty: Certainty ) : Instruction { override val memory = preState.memory override val address: SnesAddress get() = preState.address override val indicativeAddress get() = address override val relativeAddress get() = address override val sortedAddress get() = address override val nextSortedAddress get() = postState.address override val postState = opcode.mutate(this) .mutateAddress { it + bytes.size.toInt() } .withOrigin(this) override val linkedState = link()?.let { link -> opcode.mutate(this) .mutateAddress { link } .withOrigin(this) } override val showLengthSuffix get() = opcode.mode.showLengthSuffix and opcode.mnemonic.showLengthSuffix override val lengthSuffix: String? get() { if (!showLengthSuffix) { return null } return when (operandLength?.toInt()) { null -> ".?" 1 -> ".b" 2 -> ".w" 3 -> ".l" else -> null } } override val operandLength get() = opcode.mode.operandLength(preState) override fun link(): SnesAddress? { if (!opcode.link) { return null } return referencedAddress() } override fun referencedAddress() = opcode.mode.referencedAddress(this) override fun toString(): String { val (address, bytes, _, primaryMnemonic, _, suffix, operands, _, _) = print() return "${address ?: "\$xx:xxxx"} $bytes $primaryMnemonic${suffix ?: ""} ${operands?.padEnd(100, ' ') ?: ""} ($preState -> $postState)" } } fun CodeUnit.print(gameData: GameData? = null): PrintedCodeUnit { val mnemonic = opcode.mnemonic val primaryMnemonic = mnemonic.displayName val secondaryMnemonic = mnemonic.alternativeName var suffix = lengthSuffix val referencedAddress = opcode.mode.referencedAddress(this) val operandLabel = gameData?.get(referencedAddress)?.label var operands = opcode.mode.printWithLabel(this, operandLabel) if (operands == null) { operands = opcode.mode.printRaw(this) suffix = null } val state = postState?.toString() val label = gameData?.get(indicativeAddress)?.label val comment = gameData?.get(indicativeAddress)?.comment val formattedAddress = address?.toFormattedString() val bytes = bytesToString() val labelAddress = if (opcode.mode.canHaveLabel) { referencedAddress?.let { memory.toCanonical(it) } } else { null } return PrintedCodeUnit(formattedAddress, bytes, label, primaryMnemonic, secondaryMnemonic, suffix, operands, state, comment, labelAddress) } data class PrintedCodeUnit( val address: String?, val bytes: String, val label: String?, val primaryMnemonic: String, val secondaryMnemonic: String?, val suffix: String?, val operands: String?, val state: String?, val comment: String?, val labelAddress: SnesAddress? )