1
0
mirror of https://github.com/irmen/ksim65.git synced 2024-06-07 13:43:49 +00:00

disassem in debug window

This commit is contained in:
Irmen de Jong 2019-09-17 01:18:32 +02:00
parent 261b6738b4
commit bbda51fdda
10 changed files with 178 additions and 162 deletions

View File

@ -7,9 +7,9 @@ import java.util.ArrayDeque
import javax.imageio.ImageIO
import javax.swing.event.MouseInputListener
import razorvine.ksim65.IHostInterface
import razorvine.ksim65.components.MemoryComponent
import java.awt.event.*
import javax.swing.*
import javax.swing.border.LineBorder
/**
@ -101,8 +101,7 @@ private class BitmapScreenPanel : JPanel() {
fun clearScreen() {
g2d.background = ScreenDefs.BG_COLOR
g2d.clearRect(0, 0, ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT)
cursorX = 0
cursorY = 0
cursorPos(0, 0)
}
fun setPixel(x: Int, y: Int, onOff: Boolean) {
@ -134,29 +133,34 @@ private class BitmapScreenPanel : JPanel() {
)
}
fun blinkCursor(x: Int, y: Int) {
fun cursorPos(x: Int, y: Int) {
if(x!=cursorX || y!=cursorY)
cursorState=true
cursorX = x
cursorY = y
}
fun blinkCursor() {
cursorState = !cursorState
}
}
class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener {
val cyclesTf = JTextField("00000000000000")
val speedKhzTf = JTextField("0000000")
val regAtf = JTextField("000")
val regXtf = JTextField("000")
val regYtf = JTextField("000")
val regPCtf = JTextField("00000")
val regSPtf = JTextField("000")
val regPtf = JTextArea("NV-BDIZC\n000000000")
val opcodeTf = JTextField("000")
val mnemonicTf = JTextField("brk ")
val disassemTf = JTextField("00 00 00 lda (fffff),x")
private val pauseBt = JButton("Pause").also { it.actionCommand = "pause" }
init {
isFocusable = true
defaultCloseOperation = EXIT_ON_CLOSE
preferredSize = Dimension(250, 600)
preferredSize = Dimension(350, 600)
val cpuPanel = JPanel(GridBagLayout())
cpuPanel.border = BorderFactory.createTitledBorder("CPU: ${vm.cpu.name}")
val gc = GridBagConstraints()
@ -165,22 +169,22 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener {
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 opcodeLb = JLabel("opcode")
val mnemonicLb = JLabel("mnemonic")
listOf(cyclesLb, regAlb, regXlb, regYlb, regSPlb, regPClb, regPlb, opcodeLb, mnemonicLb).forEach {
val disassemLb = JLabel("Instruction")
listOf(cyclesLb, speedKhzLb, regAlb, regXlb, regYlb, regSPlb, regPClb, regPlb, disassemLb).forEach {
cpuPanel.add(it, gc)
gc.gridy++
}
gc.anchor = GridBagConstraints.WEST
gc.gridx = 1
gc.gridy = 0
listOf(cyclesTf, regAtf, regXtf, regYtf, regSPtf, regPCtf, regPtf, opcodeTf, mnemonicTf).forEach {
listOf(cyclesTf, speedKhzTf, regAtf, regXtf, regYtf, regSPtf, regPCtf, regPtf, disassemTf).forEach {
it.font = Font(Font.MONOSPACED, Font.PLAIN, 14)
it.isEditable = false
if(it is JTextField) {
@ -215,11 +219,11 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener {
when(e.actionCommand) {
"reset" -> {
vm.bus.reset()
updateCpu(vm.cpu)
updateCpu(vm.cpu, vm.ram)
}
"step" -> {
vm.stepInstruction()
updateCpu(vm.cpu)
updateCpu(vm.cpu, vm.ram)
}
"pause" -> {
vm.paused = true
@ -237,7 +241,7 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener {
}
}
fun updateCpu(cpu: Cpu6502) {
fun updateCpu(cpu: Cpu6502, mem: MemoryComponent) {
cyclesTf.text = cpu.totalCycles.toString()
regAtf.text = cpu.hexB(cpu.regA)
regXtf.text = cpu.hexB(cpu.regX)
@ -245,8 +249,7 @@ 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)
opcodeTf.text = cpu.hexB(cpu.currentOpcode)
mnemonicTf.text = cpu.currentMnemonic
disassemTf.text = cpu.disassembleOneInstruction(mem.data, cpu.regPC, mem.startAddress).first.substringAfter(' ').trim()
}
}
@ -322,7 +325,14 @@ class MainWindow(title: String) : JFrame(title), KeyListener, MouseInputListener
fun start() {
// repaint the screen's back buffer ~60 times per second
val repaintTimer = Timer(1000 / 60) { repaint() }
var cursorBlink = 0L
val repaintTimer = Timer(1000 / 60) {
repaint()
if(it.`when` - cursorBlink > 200L) {
cursorBlink = it.`when`
canvas.blinkCursor()
}
}
repaintTimer.initialDelay = 0
repaintTimer.start()
}
@ -369,15 +379,13 @@ class MainWindow(title: String) : JFrame(title), KeyListener, MouseInputListener
// the overrides required for IHostDisplay:
override fun clearScreen() = canvas.clearScreen()
override fun setPixel(x: Int, y: Int) = canvas.setPixel(x, y, true)
override fun clearPixel(x: Int, y: Int) = canvas.setPixel(x, y, false)
override fun getPixel(x: Int, y: Int) = canvas.getPixel(x, y)
override fun setChar(x: Int, y: Int, character: Char) = canvas.setChar(x, y, character)
override fun cursor(x: Int, y: Int) = canvas.cursorPos(x, y)
override fun scrollUp() = canvas.scrollUp()
override fun mouse(): IHostInterface.MouseInfo {
return IHostInterface.MouseInfo(mousePos.x, mousePos.y, leftButton, rightButton, middleButton)
}
override fun mouse() = IHostInterface.MouseInfo(mousePos.x, mousePos.y, leftButton, rightButton, middleButton)
override fun keyboard(): Char? {
return if (keyboardBuffer.isEmpty())
@ -386,7 +394,4 @@ class MainWindow(title: String) : JFrame(title), KeyListener, MouseInputListener
keyboardBuffer.pop()
}
override fun blinkCursor(x: Int, y: Int) {
canvas.blinkCursor(x, y)
}
}

View File

@ -55,12 +55,16 @@ class VirtualMachine(title: String) {
fun start() {
val timer = java.util.Timer("clock", true)
val startTime = System.currentTimeMillis()
timer.scheduleAtFixedRate(1, 1) {
if(!paused) {
repeat(10) {
repeat(5) {
stepInstruction()
}
debugWindow.updateCpu(cpu)
debugWindow.updateCpu(cpu, ram)
val duration = System.currentTimeMillis() - startTime
val speedKhz = cpu.totalCycles.toDouble() / duration
debugWindow.speedKhzTf.text = "%.1f".format(speedKhz)
}
}
}

View File

@ -143,115 +143,122 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
return hexdigits[hiNibble].toString() + hexdigits[loNibble]
}
fun disassemble(component: MemoryComponent, from: Address, to: Address) =
disassemble(component.copyOfMem(), component.startAddress, from, to)
fun disassemble(memory: MemoryComponent, from: Address, to: Address) =
disassemble(memory.data, memory.startAddress, from, to)
fun disassemble(memory: Array<UByte>, baseAddress: Address, from: Address, to: Address): List<String> {
var location = from - baseAddress
val spacing1 = " "
val spacing2 = " "
val spacing3 = " "
val result = mutableListOf<String>()
while (location <= (to - baseAddress)) {
val byte = memory[location]
var line = "\$${hexW(location+baseAddress)} ${hexB(byte)} "
location++
val opcode = instructions[byte.toInt()]
when (opcode.mode) {
AddrMode.Acc -> {
line += "$spacing1 ${opcode.mnemonic} a"
}
AddrMode.Imp -> {
line += "$spacing1 ${opcode.mnemonic}"
}
AddrMode.Imm -> {
val value = memory[location++]
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}"
}
AddrMode.Zp -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}"
}
AddrMode.Zpr -> {
// addressing mode used by the 65C02, put here for convenience
val zpAddr = memory[location++]
val rel = memory[location++]
val target =
if (rel <= 0x7f)
location + rel + baseAddress
else
location - (256 - rel) + baseAddress
line += "${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}"
}
AddrMode.Izp -> {
// addressing mode used by the 65C02, put here for convenience
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})"
}
AddrMode.IaX -> {
// addressing mode used by the 65C02, put here for convenience
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)"
}
AddrMode.ZpX -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x"
}
AddrMode.ZpY -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y"
}
AddrMode.Rel -> {
val rel = memory[location++]
val target =
if (rel <= 0x7f)
location + rel + baseAddress
else
location - (256 - rel) + baseAddress
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}"
}
AddrMode.Abs -> {
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}"
}
AddrMode.AbsX -> {
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x"
}
AddrMode.AbsY -> {
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y"
}
AddrMode.Ind -> {
val lo = memory[location++]
val hi = memory[location++]
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})"
}
AddrMode.IzX -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)"
}
AddrMode.IzY -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y"
}
}
result.add(line)
val dis = disassembleOneInstruction(memory, location, baseAddress)
result.add(dis.first)
location = dis.second
}
return result
}
fun disassembleOneInstruction(memory: Array<UByte>, address: Address, baseAddress: Address): Pair<String, Address> {
val spacing1 = " "
val spacing2 = " "
val spacing3 = " "
var location = address
val byte = memory[location]
var line = "\$${hexW(location+baseAddress)} ${hexB(byte)} "
location++
val opcode = instructions[byte.toInt()]
when (opcode.mode) {
AddrMode.Acc -> {
line += "$spacing1 ${opcode.mnemonic} a"
}
AddrMode.Imp -> {
line += "$spacing1 ${opcode.mnemonic}"
}
AddrMode.Imm -> {
val value = memory[location++]
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}"
}
AddrMode.Zp -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}"
}
AddrMode.Zpr -> {
// addressing mode used by the 65C02, put here for convenience
val zpAddr = memory[location++]
val rel = memory[location++]
val target =
if (rel <= 0x7f)
location + rel + baseAddress
else
location - (256 - rel) + baseAddress
line += "${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}"
}
AddrMode.Izp -> {
// addressing mode used by the 65C02, put here for convenience
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})"
}
AddrMode.IaX -> {
// addressing mode used by the 65C02, put here for convenience
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)"
}
AddrMode.ZpX -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x"
}
AddrMode.ZpY -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y"
}
AddrMode.Rel -> {
val rel = memory[location++]
val target =
if (rel <= 0x7f)
location + rel + baseAddress
else
location - (256 - rel) + baseAddress
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}"
}
AddrMode.Abs -> {
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}"
}
AddrMode.AbsX -> {
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x"
}
AddrMode.AbsY -> {
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y"
}
AddrMode.Ind -> {
val lo = memory[location++]
val hi = memory[location++]
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})"
}
AddrMode.IzX -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)"
}
AddrMode.IzY -> {
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y"
}
}
return Pair(line, location)
}
/**
* Reset the cpu
*/

View File

@ -10,10 +10,10 @@ interface IHostInterface {
fun setPixel(x: Int, y: Int)
fun clearPixel(x: Int, y: Int)
fun setChar(x: Int, y: Int, character: Char)
fun cursor(x: Int, y: Int)
fun scrollUp()
fun mouse(): MouseInfo
fun keyboard(): Char?
fun blinkCursor(x: Int, y: Int)
class MouseInfo(val x: Int, val y: Int, val left: Boolean, val right: Boolean, val middle: Boolean)
}

View File

@ -59,7 +59,8 @@ abstract class MemMappedComponent(val startAddress: Address, val endAddress: Add
*/
abstract class MemoryComponent(startAddress: Address, endAddress: Address) :
MemMappedComponent(startAddress, endAddress) {
abstract fun copyOfMem(): Array<UByte>
abstract val data: Array<UByte>
init {
require(startAddress and 0xff == 0 && endAddress and 0xff == 0xff) {"address range must span complete page(s)"}

View File

@ -22,11 +22,11 @@ import kotlin.math.min
* 04 pixel X pos (msb)
* 05 pixel Y pos (lsb)
* 06 pixel Y pos (msb)
* 07 read or write pixel value at pX, pY
* 08 cursor X position (r/w)
* 09 cursor Y position (r/w)
* 0a read or write character at cursor pos, updates cursor position, scrolls up if necessary
* control characters: 0x08=backspace, 0x09=tab, 0x0a=newline, 0x0c=formfeed(clear screen), 0x0d=carriagereturn
* 07 r/w pixel value at pX, pY
* 08 cursor X position
* 09 cursor Y position
* 0a r/w character at cursor pos, updates cursor position, scrolls up if necessary
* control chars: 8=backspace, 9=tab, 10=newline, 12=form feed (clear screen), 13=carriage return
*/
class Display(
startAddress: Address, endAddress: Address,
@ -49,16 +49,10 @@ class Display(
private var pixelX = 0
private var pixelY = 0
private val charMatrix = Array<ShortArray>(charHeight) { ShortArray(charWidth) } // matrix[y][x] to access
private var cursorCycles = 0
override fun clock() {
// if the system clock is synced to the display refresh,
// you *could* add a Vertical Blank interrupt here.
// for now, only the cursor blinking is controlled by this.
cursorCycles++
if(cursorCycles % 8000 == 0) {
host.blinkCursor(cursorX, cursorY)
}
}
override fun reset() {
@ -136,7 +130,7 @@ class Display(
}
0x09 -> {
// tab
cursorX = (cursorX and 248) + 8
cursorX = (cursorX and 0b11111000) + 8
if(cursorX >= charWidth) {
cursorX = 0
cursorDown()
@ -161,6 +155,7 @@ class Display(
}
}
}
host.cursor(cursorX, cursorY)
}
}
}

View File

@ -8,32 +8,26 @@ import java.net.URL
* A RAM chip with read/write memory.
*/
class Ram(startAddress: Address, endAddress: Address) : MemoryComponent(startAddress, endAddress) {
private val memory = ShortArray(endAddress - startAddress + 1)
override val data = Array<UByte>(endAddress - startAddress + 1) { 0 }
override operator fun get(address: Address): UByte = memory[address - startAddress]
override operator fun get(address: Address): UByte = data[address - startAddress]
override operator fun set(address: Address, data: UByte) {
memory[address - startAddress] = data
this.data[address - startAddress] = data
}
override fun copyOfMem(): Array<UByte> = memory.toTypedArray()
override fun clock() {}
override fun reset() {
// contents of RAM doesn't change on a reset
}
fun fill(data: UByte) {
memory.fill(data)
}
fun fill(data: UByte) = this.data.fill(data)
/**
* Load a c64-style prg program. This file type has the load address as the first two bytes.
*/
fun loadPrg(filename: String) {
loadPrg(File(filename).inputStream())
}
fun loadPrg(filename: String) = loadPrg(File(filename).inputStream())
/**
* Load a c64-style prg program. This file type has the load address as the first two bytes.
@ -43,7 +37,7 @@ class Ram(startAddress: Address, endAddress: Address) : MemoryComponent(startAdd
val loadAddress = (bytes[0].toInt() or (bytes[1].toInt() shl 8)) and 65535
val baseAddress = loadAddress - startAddress
bytes.drop(2).forEachIndexed { index, byte ->
memory[baseAddress + index] =
data[baseAddress + index] =
if (byte >= 0)
byte.toShort()
else
@ -67,13 +61,13 @@ class Ram(startAddress: Address, endAddress: Address) : MemoryComponent(startAdd
fun load(data: Array<UByte>, address: Address) =
data.forEachIndexed { index, byte ->
val baseAddress = address - startAddress
memory[baseAddress + index] = byte
this.data[baseAddress + index] = byte
}
fun load(data: ByteArray, address: Address) =
data.forEachIndexed { index, byte ->
val baseAddress = address - startAddress
memory[baseAddress + index] =
this.data[baseAddress + index] =
if (byte >= 0)
byte.toShort()
else

View File

@ -3,21 +3,16 @@ package razorvine.ksim65.components
/**
* A ROM chip (read-only memory).
*/
class Rom(startAddress: Address, endAddress: Address, data: Array<UByte>? = null) : MemoryComponent(startAddress, endAddress) {
private val memory =
if (data == null)
ShortArray(endAddress - startAddress - 1)
else
ShortArray(data.size) { index -> data[index] }
class Rom(startAddress: Address, endAddress: Address, initialData: Array<UByte>? = null) : MemoryComponent(startAddress, endAddress) {
override val data: Array<UByte> =
initialData?.copyOf() ?: Array<UByte>(endAddress - startAddress - 1) { 0 }
init {
if (data != null)
require(endAddress - startAddress + 1 == data.size) { "rom address range doesn't match size of data bytes" }
require(endAddress - startAddress + 1 == data.size) { "rom address range doesn't match size of data bytes" }
}
override operator fun get(address: Address): UByte = memory[address - startAddress]
override operator fun get(address: Address): UByte = data[address - startAddress]
override operator fun set(address: Address, data: UByte) { /* read-only */ }
override fun copyOfMem(): Array<UByte> = memory.toTypedArray()
override fun clock() {}
override fun reset() {}
}

View File

@ -16,7 +16,22 @@ start
txs
cli
; ------- print stuff
lda #10
sta DISPLAY+8
sta DISPLAY+9
ldx #5
printloop
ldy #32
printloop2
sty DISPLAY+10
iny
bne printloop2
dex
bne printloop
; ------- fill the screen
ldx #0
ldy #0
fillscreen

Binary file not shown.