102 lines
4.0 KiB
Kotlin
102 lines
4.0 KiB
Kotlin
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 SnesMemory: 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 address
|
|
val offset = address.value - entry.start
|
|
return entry.canonicalStart + offset.toInt()
|
|
}
|
|
|
|
companion object {
|
|
fun loadRom(fileData: UByteArray): SnesMemory {
|
|
val romSpace = ArrayMemorySpace(fileData)
|
|
// TODO: Auto-detect ROM type
|
|
return SnesLoRom(romSpace)
|
|
}
|
|
}
|
|
}
|
|
|
|
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): SnesMemory() {
|
|
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 lowerRomArea = (bank shl 16)
|
|
val upperRomArea = (bank shl 16) or 0x00_8000u
|
|
// Mirror upper and lower banks to the same ROM space.
|
|
// Some games map it this way, some leave the lower half completely unmapped
|
|
// While not 100% correct, we do the former to support as many games as possible
|
|
|
|
// Of note, we choose to explicitly define the upper half to be the canonical half.
|
|
|
|
add(lowerRomArea, upperRomArea, romData.range(pc, 0x00_8000u))
|
|
add(lowerRomArea + high, upperRomArea, romData.range(pc, 0x00_8000u))
|
|
add(upperRomArea, upperRomArea, romData.range(pc, 0x00_8000u))
|
|
add(upperRomArea + high, upperRomArea, 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)
|
|
}
|
|
} |