mirror of
https://github.com/irmen/ksim65.git
synced 2024-06-01 21:41:31 +00:00
fix disassembly issues, added ehBasic machine
This commit is contained in:
parent
ba8946c29c
commit
8aad9795f7
|
@ -24,6 +24,12 @@ Properties of this simulator:
|
||||||
- passes several extensive unit test suites that verify instruction and cpu flags behavior
|
- 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)
|
- 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
|
## Documentation
|
||||||
|
|
||||||
Still to be written. For now, use the source ;-)
|
Still to be written. For now, use the source ;-)
|
||||||
|
|
|
@ -42,7 +42,7 @@ dependencies {
|
||||||
|
|
||||||
application {
|
application {
|
||||||
applicationName = "ksim65vm"
|
applicationName = "ksim65vm"
|
||||||
mainClassName = "razorvine.examplemachine.SystemMainKt"
|
mainClassName = "razorvine.examplemachine.MachineMainKt"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named<Test>("test") {
|
tasks.named<Test>("test") {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package razorvine.examplemachine
|
package razorvine.examplemachine
|
||||||
|
|
||||||
|
import razorvine.ksim65.Bus
|
||||||
import razorvine.ksim65.Cpu6502
|
import razorvine.ksim65.Cpu6502
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
import javax.swing.event.MouseInputListener
|
import javax.swing.event.MouseInputListener
|
||||||
import razorvine.ksim65.IHostInterface
|
import razorvine.ksim65.IHostInterface
|
||||||
import razorvine.ksim65.components.MemoryComponent
|
|
||||||
import java.awt.event.*
|
import java.awt.event.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
@ -22,7 +22,7 @@ object ScreenDefs {
|
||||||
const val SCREEN_HEIGHT_CHARS = 30
|
const val SCREEN_HEIGHT_CHARS = 30
|
||||||
const val SCREEN_WIDTH = SCREEN_WIDTH_CHARS * 8
|
const val SCREEN_WIDTH = SCREEN_WIDTH_CHARS * 8
|
||||||
const val SCREEN_HEIGHT = SCREEN_HEIGHT_CHARS * 16
|
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 BG_COLOR = Color(0, 10, 20)
|
||||||
val FG_COLOR = Color(200, 255, 230)
|
val FG_COLOR = Color(200, 255, 230)
|
||||||
val BORDER_COLOR = Color(20, 30, 40)
|
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" }
|
private val pauseBt = JButton("Pause").also { it.actionCommand = "pause" }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
isFocusable = true
|
|
||||||
defaultCloseOperation = EXIT_ON_CLOSE
|
defaultCloseOperation = EXIT_ON_CLOSE
|
||||||
preferredSize = Dimension(350, 600)
|
preferredSize = Dimension(350, 600)
|
||||||
val cpuPanel = JPanel(GridBagLayout())
|
val cpuPanel = JPanel(GridBagLayout())
|
||||||
|
@ -222,11 +221,11 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener {
|
||||||
when(e.actionCommand) {
|
when(e.actionCommand) {
|
||||||
"reset" -> {
|
"reset" -> {
|
||||||
vm.bus.reset()
|
vm.bus.reset()
|
||||||
updateCpu(vm.cpu, vm.ram)
|
updateCpu(vm.cpu, vm.bus)
|
||||||
}
|
}
|
||||||
"step" -> {
|
"step" -> {
|
||||||
vm.stepInstruction()
|
vm.stepInstruction()
|
||||||
updateCpu(vm.cpu, vm.ram)
|
updateCpu(vm.cpu, vm.bus)
|
||||||
}
|
}
|
||||||
"pause" -> {
|
"pause" -> {
|
||||||
vm.paused = true
|
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()
|
cyclesTf.text = cpu.totalCycles.toString()
|
||||||
regAtf.text = cpu.hexB(cpu.regA)
|
regAtf.text = cpu.hexB(cpu.regA)
|
||||||
regXtf.text = cpu.hexB(cpu.regX)
|
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')
|
regPtf.text = "NV-BDIZC\n" + cpu.regP.asByte().toString(2).padStart(8, '0')
|
||||||
regPCtf.text = cpu.hexW(cpu.regPC)
|
regPCtf.text = cpu.hexW(cpu.regPC)
|
||||||
regSPtf.text = cpu.hexB(cpu.regSP)
|
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)
|
setLocationRelativeTo(null)
|
||||||
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, mutableSetOf())
|
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, mutableSetOf())
|
||||||
isVisible = true
|
isVisible = true
|
||||||
|
toFront()
|
||||||
|
requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
|
|
63
src/main/kotlin/razorvine/examplemachine/ehBasicMain.kt
Normal file
63
src/main/kotlin/razorvine/examplemachine/ehBasicMain.kt
Normal file
|
@ -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<String>) {
|
||||||
|
val machine = EhBasicMachine("KSim65 demo virtual machine - using ksim65 v${Version.version}")
|
||||||
|
machine.start()
|
||||||
|
}
|
|
@ -18,8 +18,8 @@ class VirtualMachine(title: String) {
|
||||||
private val rtc = RealTimeClock(0xd100, 0xd108)
|
private val rtc = RealTimeClock(0xd100, 0xd108)
|
||||||
private val timer = Timer(0xd200, 0xd203, cpu)
|
private val timer = Timer(0xd200, 0xd203, cpu)
|
||||||
|
|
||||||
private val hostDisplay = MainWindow(title)
|
|
||||||
private val debugWindow = DebugWindow(this)
|
private val debugWindow = DebugWindow(this)
|
||||||
|
private val hostDisplay = MainWindow(title)
|
||||||
private val display = Display(0xd000, 0xd00a, hostDisplay,
|
private val display = Display(0xd000, 0xd00a, hostDisplay,
|
||||||
ScreenDefs.SCREEN_WIDTH_CHARS, ScreenDefs.SCREEN_HEIGHT_CHARS,
|
ScreenDefs.SCREEN_WIDTH_CHARS, ScreenDefs.SCREEN_HEIGHT_CHARS,
|
||||||
ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT)
|
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.setLocation(hostDisplay.location.x+hostDisplay.width, hostDisplay.location.y)
|
||||||
debugWindow.isVisible = true
|
debugWindow.isVisible = true
|
||||||
|
hostDisplay.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
var paused = false
|
var paused = false
|
||||||
|
@ -62,10 +63,10 @@ class VirtualMachine(title: String) {
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
timer.scheduleAtFixedRate(1, 1) {
|
timer.scheduleAtFixedRate(1, 1) {
|
||||||
if(!paused) {
|
if(!paused) {
|
||||||
repeat(20) {
|
repeat(50) {
|
||||||
stepInstruction()
|
stepInstruction()
|
||||||
}
|
}
|
||||||
debugWindow.updateCpu(cpu, ram)
|
debugWindow.updateCpu(cpu, bus)
|
||||||
val duration = System.currentTimeMillis() - startTime
|
val duration = System.currentTimeMillis() - startTime
|
||||||
val speedKhz = cpu.totalCycles.toDouble() / duration
|
val speedKhz = cpu.totalCycles.toDouble() / duration
|
||||||
debugWindow.speedKhzTf.text = "%.1f".format(speedKhz)
|
debugWindow.speedKhzTf.text = "%.1f".format(speedKhz)
|
|
@ -1,9 +1,6 @@
|
||||||
package razorvine.ksim65
|
package razorvine.ksim65
|
||||||
|
|
||||||
import razorvine.ksim65.components.Address
|
import razorvine.ksim65.components.*
|
||||||
import razorvine.ksim65.components.BusComponent
|
|
||||||
import razorvine.ksim65.components.MemMappedComponent
|
|
||||||
import razorvine.ksim65.components.UByte
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The system bus that connects all other components together.
|
* The system bus that connects all other components together.
|
||||||
|
@ -73,4 +70,13 @@ class Bus {
|
||||||
it[address] = data
|
it[address] = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun memoryComponentFor(address: Address): MemoryComponent {
|
||||||
|
memComponents.forEach {
|
||||||
|
if (it is MemoryComponent && address >= it.startAddress && address <= it.endAddress) {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw NoSuchElementException()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,116 +151,130 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||||
disassemble(memory.data, memory.startAddress, from, to)
|
disassemble(memory.data, memory.startAddress, from, to)
|
||||||
|
|
||||||
fun disassemble(memory: Array<UByte>, baseAddress: Address, from: Address, to: Address): List<String> {
|
fun disassemble(memory: Array<UByte>, baseAddress: Address, from: Address, to: Address): List<String> {
|
||||||
var location = from - baseAddress
|
var location = from
|
||||||
val result = mutableListOf<String>()
|
val result = mutableListOf<String>()
|
||||||
|
|
||||||
while (location <= (to - baseAddress)) {
|
while (location <= to) {
|
||||||
val dis = disassembleOneInstruction(memory, location, baseAddress)
|
val dis = disassembleOneInstruction(memory, location, baseAddress)
|
||||||
result.add(dis.first)
|
result.add(dis.first)
|
||||||
location = dis.second
|
location += dis.second
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disassembleOneInstruction(memory: Array<UByte>, address: Address, baseAddress: Address): Pair<String, Address> {
|
fun disassembleOneInstruction(memory: Array<UByte>, address: Address, baseAddress: Address): Pair<String, Int> {
|
||||||
val spacing1 = " "
|
val spacing1 = " "
|
||||||
val spacing2 = " "
|
val spacing2 = " "
|
||||||
val spacing3 = " "
|
val spacing3 = " "
|
||||||
var location = address
|
val location = address-baseAddress
|
||||||
val byte = memory[location]
|
val byte = memory[location]
|
||||||
var line = "\$${hexW(location+baseAddress)} ${hexB(byte)} "
|
var line = "\$${hexW(location+baseAddress)} ${hexB(byte)} "
|
||||||
location++
|
|
||||||
val opcode = instructions[byte.toInt()]
|
val opcode = instructions[byte.toInt()]
|
||||||
when (opcode.mode) {
|
return when (opcode.mode) {
|
||||||
AddrMode.Acc -> {
|
AddrMode.Acc -> {
|
||||||
line += "$spacing1 ${opcode.mnemonic} a"
|
line += "$spacing1 ${opcode.mnemonic} a"
|
||||||
|
Pair(line, 1)
|
||||||
}
|
}
|
||||||
AddrMode.Imp -> {
|
AddrMode.Imp -> {
|
||||||
line += "$spacing1 ${opcode.mnemonic}"
|
line += "$spacing1 ${opcode.mnemonic}"
|
||||||
|
Pair(line, 1)
|
||||||
}
|
}
|
||||||
AddrMode.Imm -> {
|
AddrMode.Imm -> {
|
||||||
val value = memory[location++]
|
val value = memory[location+1]
|
||||||
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}"
|
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}"
|
||||||
|
Pair(line, 2)
|
||||||
}
|
}
|
||||||
AddrMode.Zp -> {
|
AddrMode.Zp -> {
|
||||||
val zpAddr = memory[location++]
|
val zpAddr = memory[location+1]
|
||||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}"
|
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}"
|
||||||
|
Pair(line, 2)
|
||||||
}
|
}
|
||||||
AddrMode.Zpr -> {
|
AddrMode.Zpr -> {
|
||||||
// addressing mode used by the 65C02, put here for convenience
|
// addressing mode used by the 65C02, put here for convenience
|
||||||
val zpAddr = memory[location++]
|
val zpAddr = memory[location+1]
|
||||||
val rel = memory[location++]
|
val rel = memory[location+2]
|
||||||
val target =
|
val target =
|
||||||
if (rel <= 0x7f)
|
if (rel <= 0x7f)
|
||||||
location + rel + baseAddress
|
location + 3 + rel + baseAddress
|
||||||
else
|
else
|
||||||
location - (256 - rel) + baseAddress
|
location + 3 - (256 - rel) + baseAddress
|
||||||
line += "${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}"
|
line += "${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}"
|
||||||
|
Pair(line, 3)
|
||||||
}
|
}
|
||||||
AddrMode.Izp -> {
|
AddrMode.Izp -> {
|
||||||
// addressing mode used by the 65C02, put here for convenience
|
// 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)})"
|
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})"
|
||||||
|
Pair(line, 2)
|
||||||
}
|
}
|
||||||
AddrMode.IaX -> {
|
AddrMode.IaX -> {
|
||||||
// addressing mode used by the 65C02, put here for convenience
|
// addressing mode used by the 65C02, put here for convenience
|
||||||
val lo = memory[location++]
|
val lo = memory[location+1]
|
||||||
val hi = memory[location++]
|
val hi = memory[location+2]
|
||||||
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)"
|
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)"
|
||||||
|
Pair(line, 3)
|
||||||
}
|
}
|
||||||
AddrMode.ZpX -> {
|
AddrMode.ZpX -> {
|
||||||
val zpAddr = memory[location++]
|
val zpAddr = memory[location+1]
|
||||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x"
|
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x"
|
||||||
|
Pair(line, 2)
|
||||||
}
|
}
|
||||||
AddrMode.ZpY -> {
|
AddrMode.ZpY -> {
|
||||||
val zpAddr = memory[location++]
|
val zpAddr = memory[location+1]
|
||||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y"
|
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y"
|
||||||
|
Pair(line, 2)
|
||||||
}
|
}
|
||||||
AddrMode.Rel -> {
|
AddrMode.Rel -> {
|
||||||
val rel = memory[location++]
|
val rel = memory[location+1]
|
||||||
val target =
|
val target =
|
||||||
if (rel <= 0x7f)
|
if (rel <= 0x7f)
|
||||||
location + rel + baseAddress
|
location + 2 + rel + baseAddress
|
||||||
else
|
else
|
||||||
location - (256 - rel) + baseAddress
|
location + 2 - (256 - rel) + baseAddress
|
||||||
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}"
|
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}"
|
||||||
|
Pair(line, 2)
|
||||||
}
|
}
|
||||||
AddrMode.Abs -> {
|
AddrMode.Abs -> {
|
||||||
val lo = memory[location++]
|
val lo = memory[location+1]
|
||||||
val hi = memory[location++]
|
val hi = memory[location+2]
|
||||||
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}"
|
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}"
|
||||||
|
Pair(line, 3)
|
||||||
}
|
}
|
||||||
AddrMode.AbsX -> {
|
AddrMode.AbsX -> {
|
||||||
val lo = memory[location++]
|
val lo = memory[location+1]
|
||||||
val hi = memory[location++]
|
val hi = memory[location+2]
|
||||||
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x"
|
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x"
|
||||||
|
Pair(line, 3)
|
||||||
}
|
}
|
||||||
AddrMode.AbsY -> {
|
AddrMode.AbsY -> {
|
||||||
val lo = memory[location++]
|
val lo = memory[location+1]
|
||||||
val hi = memory[location++]
|
val hi = memory[location+2]
|
||||||
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y"
|
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y"
|
||||||
|
Pair(line, 3)
|
||||||
}
|
}
|
||||||
AddrMode.Ind -> {
|
AddrMode.Ind -> {
|
||||||
val lo = memory[location++]
|
val lo = memory[location+1]
|
||||||
val hi = memory[location++]
|
val hi = memory[location+2]
|
||||||
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
|
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})"
|
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})"
|
||||||
|
Pair(line, 3)
|
||||||
}
|
}
|
||||||
AddrMode.IzX -> {
|
AddrMode.IzX -> {
|
||||||
val zpAddr = memory[location++]
|
val zpAddr = memory[location+1]
|
||||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)"
|
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)"
|
||||||
|
Pair(line, 2)
|
||||||
}
|
}
|
||||||
AddrMode.IzY -> {
|
AddrMode.IzY -> {
|
||||||
val zpAddr = memory[location++]
|
val zpAddr = memory[location+1]
|
||||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y"
|
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y"
|
||||||
|
Pair(line, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Pair(line, location)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -53,11 +53,6 @@ class Ram(startAddress: Address, endAddress: Address) : MemoryComponent(startAdd
|
||||||
load(bytes, address)
|
load(bytes, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load(source: URL, address: Address) {
|
|
||||||
val bytes = source.readBytes()
|
|
||||||
load(bytes, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun load(data: Array<UByte>, address: Address) =
|
fun load(data: Array<UByte>, address: Address) =
|
||||||
data.forEachIndexed { index, byte ->
|
data.forEachIndexed { index, byte ->
|
||||||
val baseAddress = address - startAddress
|
val baseAddress = address - startAddress
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package razorvine.ksim65.components
|
package razorvine.ksim65.components
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ROM chip (read-only memory).
|
* A ROM chip (read-only memory).
|
||||||
*/
|
*/
|
||||||
class Rom(startAddress: Address, endAddress: Address, initialData: Array<UByte>? = null) : MemoryComponent(startAddress, endAddress) {
|
class Rom(startAddress: Address, endAddress: Address, initialData: Array<UByte>? = null) : MemoryComponent(startAddress, endAddress) {
|
||||||
override val data: Array<UByte> =
|
override val data: Array<UByte> =
|
||||||
initialData?.copyOf() ?: Array<UByte>(endAddress - startAddress - 1) { 0 }
|
initialData?.copyOf() ?: Array<UByte>(endAddress - startAddress + 1) { 0 }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
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" }
|
||||||
|
@ -15,4 +17,26 @@ class Rom(startAddress: Address, endAddress: Address, initialData: Array<UByte>?
|
||||||
override operator fun set(address: Address, data: UByte) { /* read-only */ }
|
override operator fun set(address: Address, data: UByte) { /* read-only */ }
|
||||||
override fun clock() {}
|
override fun clock() {}
|
||||||
override fun reset() {}
|
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<UByte>) =
|
||||||
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
src/main/resources/ehbasic_C000.bin
Normal file
BIN
src/main/resources/ehbasic_C000.bin
Normal file
Binary file not shown.
|
@ -116,7 +116,7 @@ class Test6502CpuBasics {
|
||||||
val ram = Ram(0, 0xffff)
|
val ram = Ram(0, 0xffff)
|
||||||
ram[Cpu6502.RESET_vector] = 0x00
|
ram[Cpu6502.RESET_vector] = 0x00
|
||||||
ram[Cpu6502.RESET_vector +1] = 0x10
|
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)
|
ram.load(bytes, 0x1000)
|
||||||
bus.add(ram)
|
bus.add(ram)
|
||||||
bus.reset()
|
bus.reset()
|
||||||
|
|
|
@ -29,7 +29,7 @@ class TestDisassembler {
|
||||||
fun testDisassembleRockwell65C02() {
|
fun testDisassembleRockwell65C02() {
|
||||||
val cpu = Cpu65C02()
|
val cpu = Cpu65C02()
|
||||||
val memory = Ram(0, 0x0fff)
|
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)
|
memory.load(source, 0x0200)
|
||||||
val resultLines = cpu.disassemble(memory, 0x0200, 0x0250)
|
val resultLines = cpu.disassemble(memory, 0x0200, 0x0250)
|
||||||
val result = resultLines.joinToString("\n")
|
val result = resultLines.joinToString("\n")
|
||||||
|
@ -74,7 +74,7 @@ ${'$'}0250 00 brk""", result)
|
||||||
fun testDisassembleWDC65C02() {
|
fun testDisassembleWDC65C02() {
|
||||||
val cpu = Cpu65C02()
|
val cpu = Cpu65C02()
|
||||||
val memory = Ram(0, 0x0fff)
|
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)
|
memory.load(source, 0x200)
|
||||||
val resultLines = cpu.disassemble(memory, 0x0200, 0x0215)
|
val resultLines = cpu.disassemble(memory, 0x0200, 0x0215)
|
||||||
val result = resultLines.joinToString("\n")
|
val result = resultLines.joinToString("\n")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user