introduced separate Disassembler class

This commit is contained in:
Irmen de Jong 2020-02-18 22:01:12 +01:00
parent d5f533c300
commit c619ddabf1
9 changed files with 252 additions and 135 deletions

View File

@ -198,24 +198,6 @@ class C64Machine(title: String) : IVirtualMachine {
return listing.toTypedArray()
}
private fun determineRomPath(): Path {
val candidates = listOf("./roms", "~/roms/c64", "~/roms", "~/.vice/C64")
candidates.forEach {
val path = Paths.get(expandUser(it))
if (path.toFile().isDirectory) return path
}
throw FileNotFoundException("no roms directory found, tried: $candidates")
}
private fun expandUser(path: String): String {
return when {
path.startsWith("~/") -> System.getProperty("user.home")+path.substring(1)
path.startsWith("~"+File.separatorChar) -> System.getProperty("user.home")+path.substring(1)
path.startsWith("~") -> throw UnsupportedOperationException("home dir expansion not implemented for other users")
else -> path
}
}
override fun loadFileInRam(file: File, loadAddress: Address?) {
if (file.extension == "prg" && (loadAddress == null || loadAddress == 0x0801)) ram.loadPrg(file.inputStream(), null)
else ram.load(file.readBytes(), loadAddress!!)
@ -268,6 +250,26 @@ class C64Machine(title: String) : IVirtualMachine {
}
}
fun determineRomPath(): Path {
val candidates = listOf("./roms", "~/roms/c64", "~/roms", "~/.vice/C64")
candidates.forEach {
val path = Paths.get(expandUser(it))
if (path.toFile().isDirectory) return path
}
throw FileNotFoundException("no roms directory found, tried: $candidates")
}
fun expandUser(path: String): String {
return when {
path.startsWith("~/") -> System.getProperty("user.home")+path.substring(1)
path.startsWith("~"+File.separatorChar) -> System.getProperty("user.home")+path.substring(1)
path.startsWith("~") -> throw UnsupportedOperationException("home dir expansion not implemented for other users")
else -> path
}
}
fun main() {
val machine = C64Machine("virtual Commodore-64 - using KSim65 v${Version.version}")
machine.start()

View File

@ -44,6 +44,7 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("Debugger - ksim65 v
it.disabledTextColor = Color.DARK_GRAY
it.font = Font(Font.MONOSPACED, Font.PLAIN, 12)
}
private val disassembler = Disassembler(vm.cpu)
init {
contentPane.layout = GridBagLayout()
@ -217,7 +218,8 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("Debugger - ksim65 v
regSPtf.text = hexB(state.SP)
val memory = listOf(bus[state.PC], bus[state.PC+1], bus[state.PC+2]).toTypedArray()
val disassem = cpu.disassembleOneInstruction(memory, 0, state.PC).first.substringAfter(' ').trim()
val disassem = disassembler.disassembleOneInstruction(memory, 0, state.PC).first.substringAfter(' ').trim()
disassemTf.text = disassem
if (zeropageTf.isVisible || stackpageTf.isVisible) {

View File

@ -29,7 +29,14 @@ open class Cpu6502 : BusComponent() {
class StatusRegister(var C: Boolean = false, var Z: Boolean = false, var I: Boolean = false, var D: Boolean = false,
var B: Boolean = false, var V: Boolean = false, var N: Boolean = false) {
fun asInt(): Int {
return (0b00100000 or (if (N) 0b10000000 else 0) or (if (V) 0b01000000 else 0) or (if (B) 0b00010000 else 0) or (if (D) 0b00001000 else 0) or (if (I) 0b00000100 else 0) or (if (Z) 0b00000010 else 0) or (if (C) 0b00000001 else 0))
return (0b00100000
or (if (N) 0b10000000 else 0)
or (if (V) 0b01000000 else 0)
or (if (B) 0b00010000 else 0)
or (if (D) 0b00001000 else 0)
or (if (I) 0b00000100 else 0)
or (if (Z) 0b00000010 else 0)
or (if (C) 0b00000001 else 0))
}
fun fromInt(byte: Int) {
@ -128,106 +135,6 @@ open class Cpu6502 : BusComponent() {
fun removeBreakpoint(address: Address) = breakpoints.remove(address)
fun disassemble(memory: Array<UByte>, range: IntRange, baseAddress: Address): Pair<List<String>, Address> {
var offset = range.first
val result = mutableListOf<String>()
while (offset <= range.last) {
val dis = disassembleOneInstruction(memory, offset, baseAddress)
result.add(dis.first)
offset += dis.second
}
return Pair(result, offset+baseAddress)
}
fun disassembleOneInstruction(memory: Array<UByte>, offset: Int, baseAddress: Address): Pair<String, Int> {
val spacing1 = " "
val spacing2 = " "
val spacing3 = " "
val byte = memory[offset]
val line = "\$${hexW(offset+baseAddress)} ${hexB(byte)} "
val opcode = instructions[byte.toInt()]
return when (opcode.mode) {
AddrMode.Acc -> {
Pair(line+"$spacing1 ${opcode.mnemonic} a", 1)
}
AddrMode.Imp -> {
Pair(line+"$spacing1 ${opcode.mnemonic}", 1)
}
AddrMode.Imm -> {
val value = memory[offset+1]
Pair(line+"${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}", 2)
}
AddrMode.Zp -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}", 2)
}
AddrMode.Zpr -> {
// addressing mode used by the 65C02, put here for convenience rather than the subclass
val zpAddr = memory[offset+1]
val rel = memory[offset+2]
val target = (if (rel <= 0x7f) offset+3+rel+baseAddress else offset+3-(256-rel)+baseAddress) and 0xffff
Pair(line+"${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}", 3)
}
AddrMode.Izp -> {
// addressing mode used by the 65C02, put here for convenience rather than the subclass
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})", 2)
}
AddrMode.IaX -> {
// addressing mode used by the 65C02, put here for convenience rather than the subclass
val lo = memory[offset+1]
val hi = memory[offset+2]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)", 3)
}
AddrMode.ZpX -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x", 2)
}
AddrMode.ZpY -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y", 2)
}
AddrMode.Rel -> {
val rel = memory[offset+1]
val target = (if (rel <= 0x7f) offset+2+rel+baseAddress else offset+2-(256-rel)+baseAddress) and 0xffff
Pair(line+"${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}", 2)
}
AddrMode.Abs -> {
val lo = memory[offset+1]
val hi = memory[offset+2]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}", 3)
}
AddrMode.AbsX -> {
val lo = memory[offset+1]
val hi = memory[offset+2]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x", 3)
}
AddrMode.AbsY -> {
val lo = memory[offset+1]
val hi = memory[offset+2]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y", 3)
}
AddrMode.Ind -> {
val lo = memory[offset+1]
val hi = memory[offset+2]
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})", 3)
}
AddrMode.IzX -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)", 2)
}
AddrMode.IzY -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y", 2)
}
}
}
/**
* Reset the cpu
*/

View File

@ -0,0 +1,110 @@
package razorvine.ksim65
import razorvine.ksim65.components.Address
import razorvine.ksim65.components.UByte
class Disassembler(cpu: Cpu6502) {
private val instructions = cpu.instructions
fun disassemble(memory: Array<UByte>, range: IntRange, baseAddress: Address): Pair<List<String>, Address> {
var offset = range.first
val result = mutableListOf<String>()
while (offset <= range.last) {
val dis = disassembleOneInstruction(memory, offset, baseAddress)
result.add(dis.first)
offset += dis.second
}
return Pair(result, offset+baseAddress)
}
fun disassembleOneInstruction(memory: Array<UByte>, offset: Int, baseAddress: Address): Pair<String, Int> {
val spacing1 = " "
val spacing2 = " "
val spacing3 = " "
val byte = memory[offset]
val line = "\$${hexW(offset+baseAddress)} ${hexB(byte)} "
val opcode = instructions[byte.toInt()]
return when (opcode.mode) {
Cpu6502.AddrMode.Acc -> {
Pair(line+"$spacing1 ${opcode.mnemonic} a", 1)
}
Cpu6502.AddrMode.Imp -> {
Pair(line+"$spacing1 ${opcode.mnemonic}", 1)
}
Cpu6502.AddrMode.Imm -> {
val value = memory[offset+1]
Pair(line+"${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}", 2)
}
Cpu6502.AddrMode.Zp -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}", 2)
}
Cpu6502.AddrMode.Zpr -> {
// addressing mode used by the 65C02, put here for convenience rather than the subclass
val zpAddr = memory[offset+1]
val rel = memory[offset+2]
val target = (if (rel <= 0x7f) offset+3+rel+baseAddress else offset+3-(256-rel)+baseAddress) and 0xffff
Pair(line+"${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}", 3)
}
Cpu6502.AddrMode.Izp -> {
// addressing mode used by the 65C02, put here for convenience rather than the subclass
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})", 2)
}
Cpu6502.AddrMode.IaX -> {
// addressing mode used by the 65C02, put here for convenience rather than the subclass
val lo = memory[offset+1]
val hi = memory[offset+2]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)", 3)
}
Cpu6502.AddrMode.ZpX -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x", 2)
}
Cpu6502.AddrMode.ZpY -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y", 2)
}
Cpu6502.AddrMode.Rel -> {
val rel = memory[offset+1]
val target = (if (rel <= 0x7f) offset+2+rel+baseAddress else offset+2-(256-rel)+baseAddress) and 0xffff
Pair(line+"${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}", 2)
}
Cpu6502.AddrMode.Abs -> {
val lo = memory[offset+1]
val hi = memory[offset+2]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}", 3)
}
Cpu6502.AddrMode.AbsX -> {
val lo = memory[offset+1]
val hi = memory[offset+2]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x", 3)
}
Cpu6502.AddrMode.AbsY -> {
val lo = memory[offset+1]
val hi = memory[offset+2]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y", 3)
}
Cpu6502.AddrMode.Ind -> {
val lo = memory[offset+1]
val hi = memory[offset+2]
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
Pair(line+"${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})", 3)
}
Cpu6502.AddrMode.IzX -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)", 2)
}
Cpu6502.AddrMode.IzY -> {
val zpAddr = memory[offset+1]
Pair(line+"${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y", 2)
}
}
}
}

View File

@ -12,6 +12,8 @@ class Monitor(val bus: Bus, val cpu: Cpu6502) {
instr.toMap()
}
private val disassembler = Disassembler(cpu)
fun command(command: String): IVirtualMachine.MonitorCmdResult {
if (command.isEmpty()) return IVirtualMachine.MonitorCmdResult("", "", false)
@ -97,7 +99,7 @@ class Monitor(val bus: Bus, val cpu: Cpu6502) {
val start = parseNumber(addresses[0])
val end = if (addresses.size > 1) parseNumber(addresses[1]) else start
val memory = (start .. max(0xffff, end+3)).map {bus[it]}.toTypedArray()
val disassem = cpu.disassemble(memory, 0 .. end-start, start)
val disassem = disassembler.disassemble(memory, 0 .. end-start, start)
IVirtualMachine.MonitorCmdResult(disassem.first.joinToString("\n") { "d$it" }, "d$${hexW(disassem.second)}", false)
}
else -> {
@ -238,7 +240,7 @@ class Monitor(val bus: Bus, val cpu: Cpu6502) {
}
val memory = listOf(bus[address], bus[address+1], bus[address+2]).toTypedArray()
val disassem = cpu.disassembleOneInstruction(memory, 0, address)
val disassem = disassembler.disassembleOneInstruction(memory, 0, address)
return IVirtualMachine.MonitorCmdResult(disassem.first, "a$${hexW(disassem.second + address)} ", false)
}

View File

@ -19,13 +19,16 @@ abstract class FunctionalTestsBase {
cpu.addBreakpoint(0xffd2) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
cpu.addBreakpoint(0xffe4) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
cpu.addBreakpoint(0xe16f) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
cpu.addBreakpoint(0x8000) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
cpu.addBreakpoint(0xa474) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
bus.add(cpu)
bus.add(ram)
bus.reset()
}
protected fun runTest(testprogram: String) {
ram.fill(0)
// setup the irq/brk routine TODO is this the right code?
// setup the irq/brk routine and other stubbing
// http://www.softwolves.com/arkiv/cbm-hackers/7/7114.html
for(b in listOf(0x48, 0x8A, 0x48, 0x98, 0x48, 0xBA, 0xBD, 0x04,
0x01, 0x29, 0x10, 0xF0, 0x03, 0x6C, 0x16, 0x03,
0x6C, 0x14, 0x03).withIndex()) {
@ -41,9 +44,8 @@ abstract class FunctionalTestsBase {
ram[Cpu6502.RESET_vector + 1] = 0x08
ram[0x01fe] = 0xff
ram[0x01ff] = 0x7f
ram[0x8000] = 2
ram[0xa474] = 2
bus.reset()
cpu.regP.fromInt(4)
cpu.regPC = 0x0801
try {
while (cpu.totalCycles < 40000000L) {
bus.clock()

View File

@ -234,6 +234,7 @@ class Test6502CpuBasics {
val cpu = NesCpu()
val ram = Ram(0, 0xffff)
val disassembler = Disassembler(cpu)
val bytes = javaClass.getResource("nestest.nes").readBytes().drop(0x10).take(0x4000).toByteArray()
ram.load(bytes, 0x8000)
@ -247,16 +248,16 @@ class Test6502CpuBasics {
val neslog = javaClass.getResource("nestest.log").readText().lineSequence()
for(logline in neslog) {
val s = cpu.snapshot()
val s = cpu.snapshot() // TODO use cpu.tracing instead
val nesAddressHex = logline.substring(0, 4).toInt(16)
assertEquals(nesAddressHex, s.PC)
println("NES: $logline")
val disassem = cpu.disassembleOneInstruction(ram.data, s.PC, 0).first.substring(1)
val disassem = disassembler.disassembleOneInstruction(ram.data, s.PC, 0).first.substring(1)
val spaces = " ".substring(disassem.length-1)
println("EMU: $disassem $spaces A:${hexB(s.A)} X:${hexB(s.X)} Y:${hexB(s.Y)} P:${hexB(s.P.asInt())} SP:${hexB(s.SP)} PPU: 0, 0 CYC:${s.cycles}")
// TODO snapshotting as per https://forums.nesdev.com/viewtopic.php?t=19117 (i.e. BEFORE instruction gets executed):
// TODO use cpu.tracing, as per https://forums.nesdev.com/viewtopic.php?t=19117 (i.e. BEFORE instruction gets executed):
// "before fetching the first operation code byte, make an internal record of the program counter and other registers;
// after reading the final operand byte, log all the values you stored back in step (1) plus the full instruction and its disassembly."

View File

@ -1,13 +1,99 @@
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode
import razorvine.c64emu.*
import razorvine.ksim65.Bus
import razorvine.ksim65.Cpu6502
import razorvine.ksim65.components.Ram
import razorvine.ksim65.components.Rom
import kotlin.test.*
// TODO: run these tests by using the C64 machine emulation components
@Execution(ExecutionMode.CONCURRENT)
@Disabled("test code is not using C64 specific components yet")
class Test6502TestSuiteC64Specific: FunctionalTestsBase() {
// @Disabled("test code is not using C64 specific components yet")
class Test6502TestSuiteC64Specific {
val cpu: Cpu6502 = Cpu6502()
val ioPort = CpuIoPort(cpu)
val ram = Ram(0, 0xffff)
val bus: Bus
val kernalStubs = C64KernalStubs(ram)
init {
val romsPath = determineRomPath()
val chargenRom = Rom(0xd000, 0xdfff).also {
val chargenData = romsPath.resolve("chargen").toFile().readBytes()
it.load(chargenData)
}
val basicRom = Rom(0xa000, 0xbfff).also {
val basicData = romsPath.resolve("basic").toFile().readBytes()
it.load(basicData)
}
val kernalRom = Rom(0xe000, 0xffff).also {
val kernalData = romsPath.resolve("kernal").toFile().readBytes()
it.load(kernalData)
}
cpu.tracing = null
cpu.addBreakpoint(0xffd2) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
cpu.addBreakpoint(0xffe4) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
cpu.addBreakpoint(0xe16f) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
cpu.addBreakpoint(0x8000) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
cpu.addBreakpoint(0xa474) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
bus = Bus6510(ioPort, chargenRom, basicRom, kernalRom)
bus += VicII(0xd000, 0xd3ff, cpu)
bus += Cia(1, 0xdc00, 0xdcff, cpu)
bus += Cia(2, 0xdd00, 0xddff, cpu)
bus += ioPort
bus += cpu
bus += ram // note: the ROMs are mapped depending on the cpu's io port
bus.reset()
}
private fun runTest(testprogram: String) {
// setup the irq/brk routine and other stubbing
// http://www.softwolves.com/arkiv/cbm-hackers/7/7114.html
bus[0] = 47
bus[1] = 55
for(b in listOf(0x48, 0x8A, 0x48, 0x98, 0x48, 0xBA, 0xBD, 0x04,
0x01, 0x29, 0x10, 0xF0, 0x03, 0x6C, 0x16, 0x03,
0x6C, 0x14, 0x03).withIndex()) {
ram[0xff48+b.index] = b.value.toShort()
}
ram.loadPrg("src/test/kotlin/6502testsuite/$testprogram", null)
ram[0x02] = 0
ram[0xa002] = 0
ram[0xa003] = 0x80
ram[Cpu6502.IRQ_vector] = 0x48
ram[Cpu6502.IRQ_vector + 1] = 0xff
ram[Cpu6502.RESET_vector] = 0x01
ram[Cpu6502.RESET_vector + 1] = 0x08
ram[0x01fe] = 0xff
ram[0x01ff] = 0x7f
cpu.regPC = 0x0801
cpu.regP.fromInt(4)
try {
while (cpu.totalCycles < 40000000L) {
bus.clock()
}
fail("test hangs: " + cpu.snapshot())
} catch (e: Cpu6502.InstructionError) {
println(">>> INSTRUCTION ERROR: ${e.message}")
} catch (le: KernalLoadNextPart) {
return // test ok
} catch (ie: KernalInputRequired) {
fail("test failed")
}
fail("test failed")
}
@Test
fun testRegularShouldSucceed() {
// as long as this one doesn't succeed, there's something wrong with the test setup
runTest("adca")
}
@Test
fun testCia1pb6() {
@ -71,6 +157,7 @@ class Test6502TestSuiteC64Specific: FunctionalTestsBase() {
@Test
fun testCnto2() {
// todo fix: When the timer input is switched from o2 to CNT or from CNT back to o2, there must be a two clock delay until the switch is recognized.
runTest("cnto2")
}

View File

@ -1,5 +1,6 @@
import razorvine.ksim65.Cpu6502
import razorvine.ksim65.Cpu65C02
import razorvine.ksim65.Disassembler
import razorvine.ksim65.components.Ram
import kotlin.test.*
@ -9,10 +10,11 @@ class TestDisassembler {
@Test
fun testDisassembleAll6502Opcodes() {
val cpu = Cpu6502()
val disassembler = Disassembler(cpu)
val memory = Ram(0, 0xffff)
val binfile = javaClass.classLoader.getResourceAsStream("disassem_instr_test.prg")?.readBytes()!!
memory.load(binfile, 0x1000-2)
val result = cpu.disassemble(memory.data, 0x1000..0x1221, 0)
val result = disassembler.disassemble(memory.data, 0x1000..0x1221, 0)
assertEquals(256, result.first.size)
assertEquals(0x1222, result.second)
assertEquals("\$1000 69 01 adc #\$01", result.first[0])
@ -29,10 +31,11 @@ class TestDisassembler {
@Test
fun testDisassembleRockwell65C02() {
val cpu = Cpu65C02()
val disassembler = Disassembler(cpu)
val memory = Ram(0, 0x0fff)
val source = javaClass.classLoader.getResource("disassem_r65c02.bin").readBytes()
memory.load(source, 0x0200)
val disassem = cpu.disassemble(memory.data, 0x0200..0x0250, 0)
val disassem = disassembler.disassemble(memory.data, 0x0200..0x0250, 0)
assertEquals(0x251, disassem.second)
val result = disassem.first.joinToString("\n")
assertEquals("""${'$'}0200 07 12 rmb0 ${'$'}12
@ -75,10 +78,11 @@ ${'$'}0250 00 brk""", result)
@Test
fun testDisassembleWDC65C02() {
val cpu = Cpu65C02()
val disassembler = Disassembler(cpu)
val memory = Ram(0, 0x0fff)
val source = javaClass.classLoader.getResource("disassem_wdc65c02.bin").readBytes()
memory.load(source, 0x200)
val disassem = cpu.disassemble(memory.data, 0x0200..0x0215, 0)
val disassem = disassembler.disassemble(memory.data, 0x0200..0x0215, 0)
assertEquals(0x216, disassem.second)
val result = disassem.first.joinToString("\n")
assertEquals("""${'$'}0200 cb wai