From 8aad9795f7f7ea98034d396bed4674137893881b Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 19 Sep 2019 01:22:11 +0200 Subject: [PATCH] fix disassembly issues, added ehBasic machine --- README.md | 6 ++ build.gradle.kts | 2 +- .../kotlin/razorvine/examplemachine/GUI.kt | 16 ++-- .../razorvine/examplemachine/ehBasicMain.kt | 63 ++++++++++++++ .../{systemMain.kt => machineMain.kt} | 7 +- src/main/kotlin/razorvine/ksim65/Bus.kt | 14 +++- src/main/kotlin/razorvine/ksim65/Cpu6502.kt | 78 +++++++++++------- .../kotlin/razorvine/ksim65/components/Ram.kt | 5 -- .../kotlin/razorvine/ksim65/components/Rom.kt | 26 +++++- src/main/resources/ehbasic_C000.bin | Bin 0 -> 16384 bytes src/test/kotlin/Test6502CpuBasics.kt | 2 +- src/test/kotlin/TestDisassembler.kt | 4 +- 12 files changed, 167 insertions(+), 56 deletions(-) create mode 100644 src/main/kotlin/razorvine/examplemachine/ehBasicMain.kt rename src/main/kotlin/razorvine/examplemachine/{systemMain.kt => machineMain.kt} (95%) create mode 100644 src/main/resources/ehbasic_C000.bin diff --git a/README.md b/README.md index 785fe0d..62345c9 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,12 @@ Properties of this simulator: - passes several extensive unit test suites that verify instruction and cpu flags behavior - maximum simulated performance is a 6502 running at ~100 Mhz (on my machine) +## Virtual machine examples + +Two virtual example machines are included. +The default one starts with ``gradle run`` or run the ``ksim64vm`` command. +There's another one ``ehBasicMain`` that is configured to run the "enhanced 6502 basic" ROM. + ## Documentation Still to be written. For now, use the source ;-) diff --git a/build.gradle.kts b/build.gradle.kts index 2e26001..efd46ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,7 +42,7 @@ dependencies { application { applicationName = "ksim65vm" - mainClassName = "razorvine.examplemachine.SystemMainKt" + mainClassName = "razorvine.examplemachine.MachineMainKt" } tasks.named("test") { diff --git a/src/main/kotlin/razorvine/examplemachine/GUI.kt b/src/main/kotlin/razorvine/examplemachine/GUI.kt index bab3701..e4a070a 100644 --- a/src/main/kotlin/razorvine/examplemachine/GUI.kt +++ b/src/main/kotlin/razorvine/examplemachine/GUI.kt @@ -1,12 +1,12 @@ package razorvine.examplemachine +import razorvine.ksim65.Bus import razorvine.ksim65.Cpu6502 import java.awt.* import java.awt.image.BufferedImage import javax.imageio.ImageIO import javax.swing.event.MouseInputListener import razorvine.ksim65.IHostInterface -import razorvine.ksim65.components.MemoryComponent import java.awt.event.* import java.util.* import javax.swing.* @@ -22,7 +22,7 @@ object ScreenDefs { const val SCREEN_HEIGHT_CHARS = 30 const val SCREEN_WIDTH = SCREEN_WIDTH_CHARS * 8 const val SCREEN_HEIGHT = SCREEN_HEIGHT_CHARS * 16 - const val DISPLAY_PIXEL_SCALING: Double = 1.5 + const val DISPLAY_PIXEL_SCALING: Double = 1.25 val BG_COLOR = Color(0, 10, 20) val FG_COLOR = Color(200, 255, 230) val BORDER_COLOR = Color(20, 30, 40) @@ -159,7 +159,6 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener { private val pauseBt = JButton("Pause").also { it.actionCommand = "pause" } init { - isFocusable = true defaultCloseOperation = EXIT_ON_CLOSE preferredSize = Dimension(350, 600) val cpuPanel = JPanel(GridBagLayout()) @@ -222,11 +221,11 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener { when(e.actionCommand) { "reset" -> { vm.bus.reset() - updateCpu(vm.cpu, vm.ram) + updateCpu(vm.cpu, vm.bus) } "step" -> { vm.stepInstruction() - updateCpu(vm.cpu, vm.ram) + updateCpu(vm.cpu, vm.bus) } "pause" -> { vm.paused = true @@ -246,7 +245,7 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener { } } - fun updateCpu(cpu: Cpu6502, mem: MemoryComponent) { + fun updateCpu(cpu: Cpu6502, bus: Bus) { cyclesTf.text = cpu.totalCycles.toString() regAtf.text = cpu.hexB(cpu.regA) regXtf.text = cpu.hexB(cpu.regX) @@ -254,7 +253,8 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener { regPtf.text = "NV-BDIZC\n" + cpu.regP.asByte().toString(2).padStart(8, '0') regPCtf.text = cpu.hexW(cpu.regPC) regSPtf.text = cpu.hexB(cpu.regSP) - disassemTf.text = cpu.disassembleOneInstruction(mem.data, cpu.regPC, mem.startAddress).first.substringAfter(' ').trim() + val memory = bus.memoryComponentFor(cpu.regPC) + disassemTf.text = cpu.disassembleOneInstruction(memory.data, cpu.regPC, memory.startAddress).first.substringAfter(' ').trim() } } @@ -327,6 +327,8 @@ class MainWindow(title: String) : JFrame(title), KeyListener, MouseInputListener setLocationRelativeTo(null) setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, mutableSetOf()) isVisible = true + toFront() + requestFocus() } fun start() { diff --git a/src/main/kotlin/razorvine/examplemachine/ehBasicMain.kt b/src/main/kotlin/razorvine/examplemachine/ehBasicMain.kt new file mode 100644 index 0000000..67c58a2 --- /dev/null +++ b/src/main/kotlin/razorvine/examplemachine/ehBasicMain.kt @@ -0,0 +1,63 @@ +package razorvine.examplemachine + +import kotlin.concurrent.scheduleAtFixedRate +import razorvine.ksim65.Bus +import razorvine.ksim65.Cpu6502 +import razorvine.ksim65.Version +import razorvine.ksim65.components.* +import javax.swing.ImageIcon + +/** + * A virtual computer constructed from the various virtual components, + * running the 6502 Enhanced Basic ROM. + */ +class EhBasicMachine(title: String) { + val bus = Bus() + val cpu = Cpu6502(false) + val ram = Ram(0x0000, 0xbfff) + val rom = Rom(0xc000, 0xffff).also { it.load(javaClass.getResourceAsStream("/ehbasic_C000.bin").readAllBytes()) } + + private val hostDisplay = MainWindow(title) + private val display = Display(0xd000, 0xd00a, hostDisplay, + ScreenDefs.SCREEN_WIDTH_CHARS, ScreenDefs.SCREEN_HEIGHT_CHARS, + ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT) + private val keyboard = Keyboard(0xd400, 0xd400, hostDisplay) + + init { + hostDisplay.iconImage = ImageIcon(javaClass.getResource("/icon.png")).image + + bus += display + bus += keyboard + bus += rom + bus += ram + bus += cpu + bus.reset() + + hostDisplay.start() + hostDisplay.requestFocus() + } + + var paused = false + + fun stepInstruction() { + while (cpu.instrCycles > 0) bus.clock() + bus.clock() + while (cpu.instrCycles > 0) bus.clock() + } + + fun start() { + val timer = java.util.Timer("clock", true) + timer.scheduleAtFixedRate(1, 1) { + if(!paused) { + repeat(500) { + stepInstruction() + } + } + } + } +} + +fun main(args: Array) { + val machine = EhBasicMachine("KSim65 demo virtual machine - using ksim65 v${Version.version}") + machine.start() +} diff --git a/src/main/kotlin/razorvine/examplemachine/systemMain.kt b/src/main/kotlin/razorvine/examplemachine/machineMain.kt similarity index 95% rename from src/main/kotlin/razorvine/examplemachine/systemMain.kt rename to src/main/kotlin/razorvine/examplemachine/machineMain.kt index 4848196..2aa7ca8 100644 --- a/src/main/kotlin/razorvine/examplemachine/systemMain.kt +++ b/src/main/kotlin/razorvine/examplemachine/machineMain.kt @@ -18,8 +18,8 @@ class VirtualMachine(title: String) { private val rtc = RealTimeClock(0xd100, 0xd108) private val timer = Timer(0xd200, 0xd203, cpu) - private val hostDisplay = MainWindow(title) private val debugWindow = DebugWindow(this) + private val hostDisplay = MainWindow(title) private val display = Display(0xd000, 0xd00a, hostDisplay, ScreenDefs.SCREEN_WIDTH_CHARS, ScreenDefs.SCREEN_HEIGHT_CHARS, ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT) @@ -47,6 +47,7 @@ class VirtualMachine(title: String) { debugWindow.setLocation(hostDisplay.location.x+hostDisplay.width, hostDisplay.location.y) debugWindow.isVisible = true + hostDisplay.requestFocus() } var paused = false @@ -62,10 +63,10 @@ class VirtualMachine(title: String) { val startTime = System.currentTimeMillis() timer.scheduleAtFixedRate(1, 1) { if(!paused) { - repeat(20) { + repeat(50) { stepInstruction() } - debugWindow.updateCpu(cpu, ram) + debugWindow.updateCpu(cpu, bus) val duration = System.currentTimeMillis() - startTime val speedKhz = cpu.totalCycles.toDouble() / duration debugWindow.speedKhzTf.text = "%.1f".format(speedKhz) diff --git a/src/main/kotlin/razorvine/ksim65/Bus.kt b/src/main/kotlin/razorvine/ksim65/Bus.kt index a6d9d9f..5f2a994 100644 --- a/src/main/kotlin/razorvine/ksim65/Bus.kt +++ b/src/main/kotlin/razorvine/ksim65/Bus.kt @@ -1,9 +1,6 @@ package razorvine.ksim65 -import razorvine.ksim65.components.Address -import razorvine.ksim65.components.BusComponent -import razorvine.ksim65.components.MemMappedComponent -import razorvine.ksim65.components.UByte +import razorvine.ksim65.components.* /** * The system bus that connects all other components together. @@ -73,4 +70,13 @@ class Bus { it[address] = data } } + + fun memoryComponentFor(address: Address): MemoryComponent { + memComponents.forEach { + if (it is MemoryComponent && address >= it.startAddress && address <= it.endAddress) { + return it + } + } + throw NoSuchElementException() + } } diff --git a/src/main/kotlin/razorvine/ksim65/Cpu6502.kt b/src/main/kotlin/razorvine/ksim65/Cpu6502.kt index 643f807..2e007e2 100644 --- a/src/main/kotlin/razorvine/ksim65/Cpu6502.kt +++ b/src/main/kotlin/razorvine/ksim65/Cpu6502.kt @@ -151,116 +151,130 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() { disassemble(memory.data, memory.startAddress, from, to) fun disassemble(memory: Array, baseAddress: Address, from: Address, to: Address): List { - var location = from - baseAddress + var location = from val result = mutableListOf() - while (location <= (to - baseAddress)) { + while (location <= to) { val dis = disassembleOneInstruction(memory, location, baseAddress) result.add(dis.first) - location = dis.second + location += dis.second } return result } - fun disassembleOneInstruction(memory: Array, address: Address, baseAddress: Address): Pair { + fun disassembleOneInstruction(memory: Array, address: Address, baseAddress: Address): Pair { val spacing1 = " " val spacing2 = " " val spacing3 = " " - var location = address + val location = address-baseAddress val byte = memory[location] var line = "\$${hexW(location+baseAddress)} ${hexB(byte)} " - location++ val opcode = instructions[byte.toInt()] - when (opcode.mode) { + return when (opcode.mode) { AddrMode.Acc -> { line += "$spacing1 ${opcode.mnemonic} a" + Pair(line, 1) } AddrMode.Imp -> { line += "$spacing1 ${opcode.mnemonic}" + Pair(line, 1) } AddrMode.Imm -> { - val value = memory[location++] + val value = memory[location+1] line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}" + Pair(line, 2) } AddrMode.Zp -> { - val zpAddr = memory[location++] + val zpAddr = memory[location+1] line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}" + Pair(line, 2) } AddrMode.Zpr -> { // addressing mode used by the 65C02, put here for convenience - val zpAddr = memory[location++] - val rel = memory[location++] + val zpAddr = memory[location+1] + val rel = memory[location+2] val target = if (rel <= 0x7f) - location + rel + baseAddress + location + 3 + rel + baseAddress else - location - (256 - rel) + baseAddress + location + 3 - (256 - rel) + baseAddress line += "${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}" + Pair(line, 3) } AddrMode.Izp -> { // addressing mode used by the 65C02, put here for convenience - val zpAddr = memory[location++] + val zpAddr = memory[location+1] line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})" + Pair(line, 2) } AddrMode.IaX -> { // addressing mode used by the 65C02, put here for convenience - val lo = memory[location++] - val hi = memory[location++] + val lo = memory[location+1] + val hi = memory[location+2] val absAddr = lo.toInt() or (hi.toInt() shl 8) line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)" + Pair(line, 3) } AddrMode.ZpX -> { - val zpAddr = memory[location++] + val zpAddr = memory[location+1] line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x" + Pair(line, 2) } AddrMode.ZpY -> { - val zpAddr = memory[location++] + val zpAddr = memory[location+1] line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y" + Pair(line, 2) } AddrMode.Rel -> { - val rel = memory[location++] + val rel = memory[location+1] val target = if (rel <= 0x7f) - location + rel + baseAddress + location + 2 + rel + baseAddress else - location - (256 - rel) + baseAddress + location + 2 - (256 - rel) + baseAddress line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}" + Pair(line, 2) } AddrMode.Abs -> { - val lo = memory[location++] - val hi = memory[location++] + val lo = memory[location+1] + val hi = memory[location+2] val absAddr = lo.toInt() or (hi.toInt() shl 8) line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}" + Pair(line, 3) } AddrMode.AbsX -> { - val lo = memory[location++] - val hi = memory[location++] + val lo = memory[location+1] + val hi = memory[location+2] val absAddr = lo.toInt() or (hi.toInt() shl 8) line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x" + Pair(line, 3) } AddrMode.AbsY -> { - val lo = memory[location++] - val hi = memory[location++] + val lo = memory[location+1] + val hi = memory[location+2] val absAddr = lo.toInt() or (hi.toInt() shl 8) line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y" + Pair(line, 3) } AddrMode.Ind -> { - val lo = memory[location++] - val hi = memory[location++] + val lo = memory[location+1] + val hi = memory[location+2] val indirectAddr = lo.toInt() or (hi.toInt() shl 8) line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})" + Pair(line, 3) } AddrMode.IzX -> { - val zpAddr = memory[location++] + val zpAddr = memory[location+1] line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)" + Pair(line, 2) } AddrMode.IzY -> { - val zpAddr = memory[location++] + val zpAddr = memory[location+1] line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y" + Pair(line, 2) } } - return Pair(line, location) } /** diff --git a/src/main/kotlin/razorvine/ksim65/components/Ram.kt b/src/main/kotlin/razorvine/ksim65/components/Ram.kt index 5673cba..d8cf808 100644 --- a/src/main/kotlin/razorvine/ksim65/components/Ram.kt +++ b/src/main/kotlin/razorvine/ksim65/components/Ram.kt @@ -53,11 +53,6 @@ class Ram(startAddress: Address, endAddress: Address) : MemoryComponent(startAdd load(bytes, address) } - fun load(source: URL, address: Address) { - val bytes = source.readBytes() - load(bytes, address) - } - fun load(data: Array, address: Address) = data.forEachIndexed { index, byte -> val baseAddress = address - startAddress diff --git a/src/main/kotlin/razorvine/ksim65/components/Rom.kt b/src/main/kotlin/razorvine/ksim65/components/Rom.kt index 3e15d90..1afb7d1 100644 --- a/src/main/kotlin/razorvine/ksim65/components/Rom.kt +++ b/src/main/kotlin/razorvine/ksim65/components/Rom.kt @@ -1,11 +1,13 @@ package razorvine.ksim65.components +import java.io.File + /** * A ROM chip (read-only memory). */ class Rom(startAddress: Address, endAddress: Address, initialData: Array? = null) : MemoryComponent(startAddress, endAddress) { override val data: Array = - initialData?.copyOf() ?: Array(endAddress - startAddress - 1) { 0 } + initialData?.copyOf() ?: Array(endAddress - startAddress + 1) { 0 } init { require(endAddress - startAddress + 1 == data.size) { "rom address range doesn't match size of data bytes" } @@ -15,4 +17,26 @@ class Rom(startAddress: Address, endAddress: Address, initialData: Array? override operator fun set(address: Address, data: UByte) { /* read-only */ } override fun clock() {} override fun reset() {} + + /** + * load a binary program at the given address + */ + fun load(filename: String) { + val bytes = File(filename).readBytes() + load(bytes) + } + + fun load(data: Array) = + data.forEachIndexed { index, byte -> + this.data[index] = byte + } + + fun load(data: ByteArray) = + data.forEachIndexed { index, byte -> + this.data[index] = + if (byte >= 0) + byte.toShort() + else + (256 + byte).toShort() + } } diff --git a/src/main/resources/ehbasic_C000.bin b/src/main/resources/ehbasic_C000.bin new file mode 100644 index 0000000000000000000000000000000000000000..b6b01b00be3bb8b69273aa299e35f4abb5004ab1 GIT binary patch literal 16384 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 YqJk%vEnSgcvW9+^P&P$a6`3UbZ!x?^sQ>@~ literal 0 HcmV?d00001 diff --git a/src/test/kotlin/Test6502CpuBasics.kt b/src/test/kotlin/Test6502CpuBasics.kt index 53cacc7..cffb3f5 100644 --- a/src/test/kotlin/Test6502CpuBasics.kt +++ b/src/test/kotlin/Test6502CpuBasics.kt @@ -116,7 +116,7 @@ class Test6502CpuBasics { val ram = Ram(0, 0xffff) ram[Cpu6502.RESET_vector] = 0x00 ram[Cpu6502.RESET_vector +1] = 0x10 - val bytes = javaClass.getResource("bcdtest6502.bin")!! // only works on 6502, not on the 65c02 + val bytes = javaClass.getResource("bcdtest6502.bin").readBytes() // only works on 6502, not on the 65c02 ram.load(bytes, 0x1000) bus.add(ram) bus.reset() diff --git a/src/test/kotlin/TestDisassembler.kt b/src/test/kotlin/TestDisassembler.kt index f97bb9b..7db0d7e 100644 --- a/src/test/kotlin/TestDisassembler.kt +++ b/src/test/kotlin/TestDisassembler.kt @@ -29,7 +29,7 @@ class TestDisassembler { fun testDisassembleRockwell65C02() { val cpu = Cpu65C02() val memory = Ram(0, 0x0fff) - val source = javaClass.classLoader.getResource("disassem_r65c02.bin")!! + val source = javaClass.classLoader.getResource("disassem_r65c02.bin").readBytes() memory.load(source, 0x0200) val resultLines = cpu.disassemble(memory, 0x0200, 0x0250) val result = resultLines.joinToString("\n") @@ -74,7 +74,7 @@ ${'$'}0250 00 brk""", result) fun testDisassembleWDC65C02() { val cpu = Cpu65C02() val memory = Ram(0, 0x0fff) - val source = javaClass.classLoader.getResource("disassem_wdc65c02.bin")!! + val source = javaClass.classLoader.getResource("disassem_wdc65c02.bin").readBytes() memory.load(source, 0x200) val resultLines = cpu.disassemble(memory, 0x0200, 0x0215) val result = resultLines.joinToString("\n")