mirror of
https://github.com/irmen/ksim65.git
synced 2024-11-15 09:05:52 +00:00
introduced separate Disassembler class
This commit is contained in:
parent
d5f533c300
commit
c619ddabf1
@ -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()
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
*/
|
||||
|
110
src/main/kotlin/razorvine/ksim65/Disassembler.kt
Normal file
110
src/main/kotlin/razorvine/ksim65/Disassembler.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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."
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user