From c3c40bc73e3d00ee4b13de5d78db783f8870c614 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 4 Jan 2020 13:45:16 +0100 Subject: [PATCH] optimizations in debugwindow --- .idea/.gitignore | 5 + .idea/jarRepositories.xml | 30 +++ .idea/ksim65.iml | 2 + .idea/markdown-navigator-enh.xml | 29 +++ .idea/markdown-navigator.xml | 55 +++++ .idea/misc.xml | 3 + .idea/uiDesigner.xml | 124 ++++++++++ src/main/kotlin/razorvine/c64emu/c64Main.kt | 2 +- .../razorvine/examplemachines/DebugWindow.kt | 233 ++++++++++++++++++ .../kotlin/razorvine/examplemachines/GUI.kt | 217 ---------------- 10 files changed, 482 insertions(+), 218 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/ksim65.iml create mode 100644 .idea/markdown-navigator-enh.xml create mode 100644 .idea/markdown-navigator.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 src/main/kotlin/razorvine/examplemachines/DebugWindow.kt diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..f009303 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ + +# Project exclude paths +/. +# Project exclude paths +/. \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..3fd27b3 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/ksim65.iml b/.idea/ksim65.iml new file mode 100644 index 0000000..78b2cc5 --- /dev/null +++ b/.idea/ksim65.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/markdown-navigator-enh.xml b/.idea/markdown-navigator-enh.xml new file mode 100644 index 0000000..12fb99d --- /dev/null +++ b/.idea/markdown-navigator-enh.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml new file mode 100644 index 0000000..d6df267 --- /dev/null +++ b/.idea/markdown-navigator.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 6c9f64a..70064e8 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,8 @@ + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/kotlin/razorvine/c64emu/c64Main.kt b/src/main/kotlin/razorvine/c64emu/c64Main.kt index a298c6f..2094a3b 100644 --- a/src/main/kotlin/razorvine/c64emu/c64Main.kt +++ b/src/main/kotlin/razorvine/c64emu/c64Main.kt @@ -39,9 +39,9 @@ class C64Machine(title: String) : IVirtualMachine { val kernalRom = Rom(0xe000, 0xffff).also { it.load(kernalData) } val cpuIoPort = CpuIoPort(cpu) + private val monitor = Monitor(bus, cpu) private val debugWindow = DebugWindow(this) private val hostDisplay = MainC64Window(title, chargenData, ram, cpu, cia1) - private val monitor = Monitor(bus, cpu) private var paused = false init { diff --git a/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt b/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt new file mode 100644 index 0000000..8c02574 --- /dev/null +++ b/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt @@ -0,0 +1,233 @@ +package razorvine.examplemachines + +import razorvine.ksim65.* +import java.awt.* +import java.awt.event.ActionEvent +import java.awt.event.ActionListener +import java.awt.event.WindowEvent +import java.io.File +import javax.swing.* +import javax.swing.text.DefaultCaret + + +class DebugWindow(private val vm: IVirtualMachine) : JFrame("Debugger - ksim65 v${Version.version}"), + ActionListener { + + private class UnaliasedTextBox(rows: Int, columns: Int) : JTextArea(rows, columns) { + override fun paintComponent(g: Graphics) { + g as Graphics2D + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF) + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF) + super.paintComponent(g) + } + } + + private val cyclesTf = JTextField("00000000000000") + private val speedKhzTf = JTextField("0000000") + private val regAtf = JTextField("000") + private val regXtf = JTextField("000") + private val regYtf = JTextField("000") + private val regPCtf = JTextField("00000") + private val regSPtf = JTextField("000") + private val regPtf = JTextArea("NV-BDIZC\n000000000") + private val disassemTf = JTextField("00 00 00 lda (fffff),x") + private val pauseBt = JButton("Pause").also { it.actionCommand = "pause" } + private val zeropageTf = UnaliasedTextBox(8, 102).also { + it.border = BorderFactory.createEtchedBorder() + it.isEnabled = false + it.disabledTextColor = Color.DARK_GRAY + it.font = Font(Font.MONOSPACED, Font.PLAIN, 12) + } + private val stackpageTf = UnaliasedTextBox(8, 102).also { + it.border = BorderFactory.createEtchedBorder() + it.isEnabled = false + it.disabledTextColor = Color.DARK_GRAY + it.font = Font(Font.MONOSPACED, Font.PLAIN, 12) + } + + init { + contentPane.layout = GridBagLayout() + defaultCloseOperation = EXIT_ON_CLOSE + val cpuPanel = JPanel(GridBagLayout()) + cpuPanel.border = BorderFactory.createTitledBorder("CPU: ${vm.cpu.name}") + val gc = GridBagConstraints() + gc.insets = Insets(2, 2, 2, 2) + gc.anchor = GridBagConstraints.EAST + gc.gridx = 0 + gc.gridy = 0 + val cyclesLb = JLabel("cycles") + val speedKhzLb = JLabel("speed (kHz)") + val regAlb = JLabel("A") + val regXlb = JLabel("X") + val regYlb = JLabel("Y") + val regSPlb = JLabel("SP") + val regPClb = JLabel("PC") + val regPlb = JLabel("Status") + val disassemLb = JLabel("Instruction") + listOf(cyclesLb, speedKhzLb, regAlb, regXlb, regYlb, regSPlb, regPClb, disassemLb, regPlb).forEach { + cpuPanel.add(it, gc) + gc.gridy++ + } + gc.anchor = GridBagConstraints.WEST + gc.gridx = 1 + gc.gridy = 0 + listOf(cyclesTf, speedKhzTf, regAtf, regXtf, regYtf, regSPtf, regPCtf, disassemTf, regPtf).forEach { + it.font = Font(Font.MONOSPACED, Font.PLAIN, 14) + it.disabledTextColor = Color.DARK_GRAY + it.isEnabled = false + if (it is JTextField) { + it.columns = it.text.length + } else if (it is JTextArea) { + it.border = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY), + BorderFactory.createEmptyBorder(2, 2, 2, 2)) + } + cpuPanel.add(it, gc) + gc.gridy++ + } + + val buttonPanel = JPanel(FlowLayout()) + buttonPanel.border = BorderFactory.createTitledBorder("Control") + + val loadBt = JButton("Inject program").also { it.actionCommand = "inject" } + val resetBt = JButton("Reset").also { it.actionCommand = "reset" } + val stepBt = JButton("Step").also { it.actionCommand = "step" } + val irqBt = JButton("IRQ").also { it.actionCommand = "irq" } + val nmiBt = JButton("NMI").also { it.actionCommand = "nmi" } + val quitBt = JButton("Quit").also { it.actionCommand = "quit" } + listOf(loadBt, resetBt, irqBt, nmiBt, pauseBt, stepBt, quitBt).forEach { + it.addActionListener(this) + buttonPanel.add(it) + } + + val zeropagePanel = JPanel() + zeropagePanel.layout = BoxLayout(zeropagePanel, BoxLayout.Y_AXIS) + zeropagePanel.border = BorderFactory.createTitledBorder("Zeropage and Stack") + val showZp = JCheckBox("show Zero page and Stack dumps", true) + showZp.addActionListener { + val visible = (it.source as JCheckBox).isSelected + zeropageTf.isVisible = visible + stackpageTf.isVisible = visible + } + zeropagePanel.add(showZp) + zeropagePanel.add(zeropageTf) + zeropagePanel.add(stackpageTf) + + val monitorPanel = JPanel() + monitorPanel.layout = BoxLayout(monitorPanel, BoxLayout.Y_AXIS) + monitorPanel.border = BorderFactory.createTitledBorder("Built-in Monitor") + val output = JTextArea(6, 80) + output.font = Font(Font.MONOSPACED, Font.PLAIN, 14) + output.isEditable = false + val outputScroll = JScrollPane(output) + monitorPanel.add(outputScroll) + outputScroll.verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_ALWAYS + (output.caret as DefaultCaret).updatePolicy = DefaultCaret.ALWAYS_UPDATE + val input = JTextField(50) + input.border = BorderFactory.createLineBorder(Color.LIGHT_GRAY) + input.font = Font(Font.MONOSPACED, Font.PLAIN, 14) + input.addActionListener { + output.append("\n") + val command = input.text.trim() + val result = vm.executeMonitorCommand(command) + if (result.echo) output.append("> $command\n") + output.append(result.output) + input.text = result.prompt + } + monitorPanel.add(input) + output.append(vm.executeMonitorCommand("h").output) + + gc.gridx = 0 + gc.gridy = 0 + gc.fill = GridBagConstraints.BOTH + contentPane.add(cpuPanel, gc) + gc.gridy++ + contentPane.add(zeropagePanel, gc) + gc.gridy++ + contentPane.add(monitorPanel, gc) + gc.gridy++ + contentPane.add(buttonPanel, gc) + pack() + } + + override fun actionPerformed(e: ActionEvent) { + when (e.actionCommand) { + "inject" -> { + val chooser = JFileChooser() + chooser.dialogTitle = "Choose binary program or .prg to load" + chooser.currentDirectory = File(".") + chooser.isMultiSelectionEnabled = false + val result = chooser.showOpenDialog(this) + if (result == JFileChooser.APPROVE_OPTION) { + if (chooser.selectedFile.extension == "prg") { + vm.loadFileInRam(chooser.selectedFile, null) + } else { + val addressStr = JOptionPane.showInputDialog(this, + "The selected file isn't a .prg.\nSpecify memory load address (hexadecimal) manually.", + "Load address", JOptionPane.QUESTION_MESSAGE, null, null, + "$") as String + + val loadAddress = Integer.parseInt(addressStr.removePrefix("$"), 16) + vm.loadFileInRam(chooser.selectedFile, loadAddress) + } + } + + } + "reset" -> { + vm.bus.reset() + updateCpu(vm.cpu, vm.bus) + } + "step" -> { + vm.step() + updateCpu(vm.cpu, vm.bus) + } + "pause" -> { + vm.pause(true) + pauseBt.actionCommand = "continue" + pauseBt.text = "Continue" + } + "continue" -> { + vm.pause(false) + pauseBt.actionCommand = "pause" + pauseBt.text = "Pause" + } + "irq" -> vm.cpu.irq() + "nmi" -> vm.cpu.nmi() + "quit" -> { + dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING)) + } + } + } + + fun updateCpu(cpu: Cpu6502, bus: Bus) { + val state = cpu.snapshot() + cyclesTf.text = state.cycles.toString() + regAtf.text = hexB(state.A) + regXtf.text = hexB(state.X) + regYtf.text = hexB(state.Y) + regPtf.text = "NV-BDIZC\n"+state.P.asInt().toString(2).padStart(8, '0') + regPCtf.text = hexW(state.PC) + regSPtf.text = hexB(state.SP) + val memory = bus.memoryComponentFor(state.PC) + disassemTf.text = cpu.disassembleOneInstruction(memory.data, state.PC, memory.startAddress).first.substringAfter(' ').trim() + + if (zeropageTf.isVisible || stackpageTf.isVisible) { + val pages = vm.getZeroAndStackPages() + if (pages.isNotEmpty()) { + val zpLines = (0..0xff step 32).map { location -> + " ${'$'}${location.toString(16).padStart(2, '0')} "+((0..31).joinToString(" ") { lineoffset -> + pages[location+lineoffset].toString(16).padStart(2, '0') + }) + } + val stackLines = (0x100..0x1ff step 32).map { location -> + "${'$'}${location.toString(16).padStart(2, '0')} "+((0..31).joinToString(" ") { lineoffset -> + pages[location+lineoffset].toString(16).padStart(2, '0') + }) + } + zeropageTf.text = zpLines.joinToString("\n") + stackpageTf.text = stackLines.joinToString("\n") + } + } + + speedKhzTf.text = "%.1f".format(cpu.averageSpeedKhzSinceReset) + } +} diff --git a/src/main/kotlin/razorvine/examplemachines/GUI.kt b/src/main/kotlin/razorvine/examplemachines/GUI.kt index f72a181..017a21d 100644 --- a/src/main/kotlin/razorvine/examplemachines/GUI.kt +++ b/src/main/kotlin/razorvine/examplemachines/GUI.kt @@ -4,8 +4,6 @@ import razorvine.ksim65.* import java.awt.* import java.awt.event.* import java.awt.image.BufferedImage -import java.io.File -import java.lang.Integer.parseInt import java.util.* import javax.imageio.ImageIO import javax.swing.* @@ -139,221 +137,6 @@ private class BitmapScreenPanel : JPanel() { } } -private class UnaliasedTextBox(rows: Int, columns: Int) : JTextArea(rows, columns) { - override fun paintComponent(g: Graphics) { - g as Graphics2D - g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF) - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF) - super.paintComponent(g) - } -} - -class DebugWindow(private val vm: IVirtualMachine) : JFrame("Debugger - ksim65 v${Version.version}"), ActionListener { - private val cyclesTf = JTextField("00000000000000") - private val speedKhzTf = JTextField("0000000") - private val regAtf = JTextField("000") - private val regXtf = JTextField("000") - private val regYtf = JTextField("000") - private val regPCtf = JTextField("00000") - private val regSPtf = JTextField("000") - private val regPtf = JTextArea("NV-BDIZC\n000000000") - private val disassemTf = JTextField("00 00 00 lda (fffff),x") - private val pauseBt = JButton("Pause").also { it.actionCommand = "pause" } - private val zeropageTf = UnaliasedTextBox(8, 102).also { - it.border = BorderFactory.createEtchedBorder() - it.isEnabled = false - it.disabledTextColor = Color.DARK_GRAY - it.font = Font(Font.MONOSPACED, Font.PLAIN, 12) - } - private val stackpageTf = UnaliasedTextBox(8, 102).also { - it.border = BorderFactory.createEtchedBorder() - it.isEnabled = false - it.disabledTextColor = Color.DARK_GRAY - it.font = Font(Font.MONOSPACED, Font.PLAIN, 12) - } - - init { - contentPane.layout = GridBagLayout() - defaultCloseOperation = EXIT_ON_CLOSE - val cpuPanel = JPanel(GridBagLayout()) - cpuPanel.border = BorderFactory.createTitledBorder("CPU: ${vm.cpu.name}") - val gc = GridBagConstraints() - gc.insets = Insets(2, 2, 2, 2) - gc.anchor = GridBagConstraints.EAST - gc.gridx = 0 - gc.gridy = 0 - val cyclesLb = JLabel("cycles") - val speedKhzLb = JLabel("speed (kHz)") - val regAlb = JLabel("A") - val regXlb = JLabel("X") - val regYlb = JLabel("Y") - val regSPlb = JLabel("SP") - val regPClb = JLabel("PC") - val regPlb = JLabel("Status") - val disassemLb = JLabel("Instruction") - listOf(cyclesLb, speedKhzLb, regAlb, regXlb, regYlb, regSPlb, regPClb, disassemLb, regPlb).forEach { - cpuPanel.add(it, gc) - gc.gridy++ - } - gc.anchor = GridBagConstraints.WEST - gc.gridx = 1 - gc.gridy = 0 - listOf(cyclesTf, speedKhzTf, regAtf, regXtf, regYtf, regSPtf, regPCtf, disassemTf, regPtf).forEach { - it.font = Font(Font.MONOSPACED, Font.PLAIN, 14) - it.disabledTextColor = Color.DARK_GRAY - it.isEnabled = false - if (it is JTextField) { - it.columns = it.text.length - } else if (it is JTextArea) { - it.border = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY), - BorderFactory.createEmptyBorder(2, 2, 2, 2)) - } - cpuPanel.add(it, gc) - gc.gridy++ - } - - val buttonPanel = JPanel(FlowLayout()) - buttonPanel.border = BorderFactory.createTitledBorder("Control") - - val loadBt = JButton("Inject program").also { it.actionCommand = "inject" } - val resetBt = JButton("Reset").also { it.actionCommand = "reset" } - val stepBt = JButton("Step").also { it.actionCommand = "step" } - val irqBt = JButton("IRQ").also { it.actionCommand = "irq" } - val nmiBt = JButton("NMI").also { it.actionCommand = "nmi" } - val quitBt = JButton("Quit").also { it.actionCommand = "quit" } - listOf(loadBt, resetBt, irqBt, nmiBt, pauseBt, stepBt, quitBt).forEach { - it.addActionListener(this) - buttonPanel.add(it) - } - - val zeropagePanel = JPanel() - zeropagePanel.layout = BoxLayout(zeropagePanel, BoxLayout.Y_AXIS) - zeropagePanel.border = BorderFactory.createTitledBorder("Zeropage and Stack") - val showZp = JCheckBox("show Zero page and Stack dumps", true) - showZp.addActionListener { - val visible = (it.source as JCheckBox).isSelected - zeropageTf.isVisible = visible - stackpageTf.isVisible = visible - } - zeropagePanel.add(showZp) - zeropagePanel.add(zeropageTf) - zeropagePanel.add(stackpageTf) - - val monitorPanel = JPanel() - monitorPanel.layout = BoxLayout(monitorPanel, BoxLayout.Y_AXIS) - monitorPanel.border = BorderFactory.createTitledBorder("Built-in Monitor") - val output = JTextArea(6, 80) - output.font = Font(Font.MONOSPACED, Font.PLAIN, 14) - output.isEditable = false - val outputScroll = JScrollPane(output) - monitorPanel.add(outputScroll) - outputScroll.verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_ALWAYS - val input = JTextField(50) - input.border = BorderFactory.createLineBorder(Color.LIGHT_GRAY) - input.font = Font(Font.MONOSPACED, Font.PLAIN, 14) - input.addActionListener { - output.append("\n") - val command = input.text.trim() - val result = vm.executeMonitorCommand(command) - if (result.echo) output.append("> $command\n") - output.append(result.output) - input.text = result.prompt - } - monitorPanel.add(input) - - gc.gridx = 0 - gc.gridy = 0 - gc.fill = GridBagConstraints.BOTH - contentPane.add(cpuPanel, gc) - gc.gridy++ - contentPane.add(zeropagePanel, gc) - gc.gridy++ - contentPane.add(monitorPanel, gc) - gc.gridy++ - contentPane.add(buttonPanel, gc) - pack() - } - - override fun actionPerformed(e: ActionEvent) { - when (e.actionCommand) { - "inject" -> { - val chooser = JFileChooser() - chooser.dialogTitle = "Choose binary program or .prg to load" - chooser.currentDirectory = File(".") - chooser.isMultiSelectionEnabled = false - val result = chooser.showOpenDialog(this) - if (result == JFileChooser.APPROVE_OPTION) { - if (chooser.selectedFile.extension == "prg") { - vm.loadFileInRam(chooser.selectedFile, null) - } else { - val addressStr = JOptionPane.showInputDialog(this, - "The selected file isn't a .prg.\nSpecify memory load address (hexadecimal) manually.", - "Load address", JOptionPane.QUESTION_MESSAGE, null, null, - "$") as String - - val loadAddress = parseInt(addressStr.removePrefix("$"), 16) - vm.loadFileInRam(chooser.selectedFile, loadAddress) - } - } - - } - "reset" -> { - vm.bus.reset() - updateCpu(vm.cpu, vm.bus) - } - "step" -> { - vm.step() - updateCpu(vm.cpu, vm.bus) - } - "pause" -> { - vm.pause(true) - pauseBt.actionCommand = "continue" - pauseBt.text = "Continue" - } - "continue" -> { - vm.pause(false) - pauseBt.actionCommand = "pause" - pauseBt.text = "Pause" - } - "irq" -> vm.cpu.irq() - "nmi" -> vm.cpu.nmi() - "quit" -> { - dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING)) - } - } - } - - fun updateCpu(cpu: Cpu6502, bus: Bus) { - val state = cpu.snapshot() - cyclesTf.text = state.cycles.toString() - regAtf.text = hexB(state.A) - regXtf.text = hexB(state.X) - regYtf.text = hexB(state.Y) - regPtf.text = "NV-BDIZC\n"+state.P.asInt().toString(2).padStart(8, '0') - regPCtf.text = hexW(state.PC) - regSPtf.text = hexB(state.SP) - val memory = bus.memoryComponentFor(state.PC) - disassemTf.text = cpu.disassembleOneInstruction(memory.data, state.PC, memory.startAddress).first.substringAfter(' ').trim() - val pages = vm.getZeroAndStackPages() - if (pages.isNotEmpty()) { - val zpLines = (0..0xff step 32).map { location -> - " ${'$'}${location.toString(16).padStart(2, '0')} "+((0..31).joinToString(" ") { lineoffset -> - pages[location+lineoffset].toString(16).padStart(2, '0') - }) - } - val stackLines = (0x100..0x1ff step 32).map { location -> - "${'$'}${location.toString(16).padStart(2, '0')} "+((0..31).joinToString(" ") { lineoffset -> - pages[location+lineoffset].toString(16).padStart(2, '0') - }) - } - zeropageTf.text = zpLines.joinToString("\n") - stackpageTf.text = stackLines.joinToString("\n") - } - - speedKhzTf.text = "%.1f".format(cpu.averageSpeedKhzSinceReset) - } -} - class MainWindow(title: String) : JFrame(title), KeyListener, MouseInputListener, IHostInterface { private val canvas = BitmapScreenPanel()