fix disassembler address display

This commit is contained in:
Irmen de Jong 2019-09-14 17:25:41 +02:00
parent b64c382968
commit 66c4033eb4
7 changed files with 121 additions and 60 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2016 Irmen de Jong Copyright (c) 2019 Irmen de Jong
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,8 +1,7 @@
[![saythanks](https://img.shields.io/badge/say-thanks-ff69b4.svg)](https://saythanks.io/to/irmen) [![saythanks](https://img.shields.io/badge/say-thanks-ff69b4.svg)](https://saythanks.io/to/irmen)
[![Build Status](https://travis-ci.org/irmen/ksim65.svg?branch=master)](https://travis-ci.org/irmen/ksim65) [![Build Status](https://travis-ci.org/irmen/ksim65.svg?branch=master)](https://travis-ci.org/irmen/ksim65)
KSim65 - Kotlin 6502/65C02 microprocessor simulator # KSim65 - Kotlin/JVM 6502/65C02 microprocessor simulator
===================================================
*Written by Irmen de Jong (irmen@razorvine.net)* *Written by Irmen de Jong (irmen@razorvine.net)*
@ -11,8 +10,20 @@ KSim65 - Kotlin 6502/65C02 microprocessor simulator
![6502](https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/KL_MOS_6502.jpg/320px-KL_MOS_6502.jpg) ![6502](https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/KL_MOS_6502.jpg/320px-KL_MOS_6502.jpg)
This is a Kotlin library to simulate the 8-bit 6502 and 65C02 microprocessors of the early 80's. This is a Kotlin/JVM library that simulates the 8-bit 6502 and 65C02 microprocessors,
which became very popular in the the early 1980's.
The simulation is cycle precise, includes BCD mode, and all opcodes are implemented (including the 'illegal' opcodes of the 6502). Properties of this simulator:
On my machine the library can simulate a 6502 running at up to ~100Mhz. - Written in Kotlin. It is low-level code, but hopefully still readable :-)
- Designed to simulate hardware components (bus, cpu, memory, i/o controllers)
- IRQ and NMI simulation
- Aims to be clock cycle-precise (not yet 100% correct right now)
- Aims to implements all 6502 and 65c02 instructions, including the 'illegal' 6502 instructions (not yet done)
- correct BCD mode for adc/sbc instructions on both cpu types
- 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)
## Documentation
Still to be written. For now,

View File

@ -14,7 +14,7 @@ fun main(args: Array<String>) {
internal fun printSoftwareHeader() { internal fun printSoftwareHeader() {
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim() val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
println("\nKSim65 6502 cpu simulator v$buildVersion by Irmen de Jong (irmen@razorvine.net)") println("\nKSim65 6502 cpu simulator v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") println("This software is free and licensed under the MIT open-source license\n")
} }
@ -70,8 +70,7 @@ private fun startSimulator(args: Array<String>) {
bus.clock() bus.clock()
} }
} catch (ix: Cpu6502.InstructionError) { } catch (ix: Cpu6502.InstructionError) {
println("HMMM $ix") println("Hmmm... $ix")
// ignore
} }
ram.hexDump(0x1000, 0x1020) ram.hexDump(0x1000, 0x1020)

View File

@ -133,16 +133,16 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
disassemble(component.cloneContents(), component.startAddress, from, to) disassemble(component.cloneContents(), component.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 address = from - baseAddress var location = from - baseAddress
val spacing1 = " " val spacing1 = " "
val spacing2 = " " val spacing2 = " "
val spacing3 = " " val spacing3 = " "
val result = mutableListOf<String>() val result = mutableListOf<String>()
while (address <= (to - baseAddress)) { while (location <= (to - baseAddress)) {
val byte = memory[address] val byte = memory[location]
var line = "\$${hexW(address)} ${hexB(byte)} " var line = "\$${hexW(location+baseAddress)} ${hexB(byte)} "
address++ location++
val opcode = instructions[byte.toInt()] val opcode = instructions[byte.toInt()]
when (opcode.mode) { when (opcode.mode) {
AddrMode.Acc -> { AddrMode.Acc -> {
@ -152,83 +152,83 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
line += "$spacing1 ${opcode.mnemonic}" line += "$spacing1 ${opcode.mnemonic}"
} }
AddrMode.Imm -> { AddrMode.Imm -> {
val value = memory[address++] val value = memory[location++]
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}" line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}"
} }
AddrMode.Zp -> { AddrMode.Zp -> {
val zpAddr = memory[address++] val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}" line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}"
} }
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[address++] val zpAddr = memory[location++]
val rel = memory[address++] val rel = memory[location++]
val target = val target =
if (rel <= 0x7f) if (rel <= 0x7f)
address + rel location + rel + baseAddress
else else
address - (256 - rel) location - (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)}"
} }
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[address++] val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})" line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})"
} }
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[address++] val lo = memory[location++]
val hi = memory[address++] val hi = memory[location++]
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)"
} }
AddrMode.ZpX -> { AddrMode.ZpX -> {
val zpAddr = memory[address++] val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x" line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x"
} }
AddrMode.ZpY -> { AddrMode.ZpY -> {
val zpAddr = memory[address++] val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y" line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y"
} }
AddrMode.Rel -> { AddrMode.Rel -> {
val rel = memory[address++] val rel = memory[location++]
val target = val target =
if (rel <= 0x7f) if (rel <= 0x7f)
address + rel location + rel + baseAddress
else else
address - (256 - rel) location - (256 - rel) + baseAddress
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}" line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}"
} }
AddrMode.Abs -> { AddrMode.Abs -> {
val lo = memory[address++] val lo = memory[location++]
val hi = memory[address++] val hi = memory[location++]
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)}"
} }
AddrMode.AbsX -> { AddrMode.AbsX -> {
val lo = memory[address++] val lo = memory[location++]
val hi = memory[address++] val hi = memory[location++]
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"
} }
AddrMode.AbsY -> { AddrMode.AbsY -> {
val lo = memory[address++] val lo = memory[location++]
val hi = memory[address++] val hi = memory[location++]
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"
} }
AddrMode.Ind -> { AddrMode.Ind -> {
val lo = memory[address++] val lo = memory[location++]
val hi = memory[address++] val hi = memory[location++]
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)})"
} }
AddrMode.IzX -> { AddrMode.IzX -> {
val zpAddr = memory[address++] val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)" line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)"
} }
AddrMode.IzY -> { AddrMode.IzY -> {
val zpAddr = memory[address++] val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y" line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y"
} }
} }

View File

@ -1,5 +1,6 @@
import razorvine.ksim65.components.Bus import razorvine.ksim65.components.Bus
import razorvine.ksim65.components.Cpu6502 import razorvine.ksim65.components.Cpu6502
import razorvine.ksim65.components.Cpu65C02
import razorvine.ksim65.components.Ram import razorvine.ksim65.components.Ram
import kotlin.test.* import kotlin.test.*
import kotlin.system.measureNanoTime import kotlin.system.measureNanoTime
@ -9,7 +10,7 @@ import kotlin.test.assertEquals
class Test6502CpuBasics { class Test6502CpuBasics {
@Test @Test
fun testCpuFlagsAfterReset() { fun testCpuFlagsAfterReset6502() {
val cpu = Cpu6502(true) val cpu = Cpu6502(true)
val bus = Bus() val bus = Bus()
bus.add(cpu) bus.add(cpu)
@ -27,7 +28,25 @@ class Test6502CpuBasics {
} }
@Test @Test
fun testCpuPerformance() { fun testCpuFlagsAfterReset65c02() {
val cpu = Cpu65C02(true)
val bus = Bus()
bus.add(cpu)
cpu.reset()
assertEquals(0xfd, cpu.SP)
assertEquals(0xffff, cpu.PC)
assertEquals(0, cpu.totalCycles)
assertEquals(8, cpu.instrCycles)
assertEquals(0, cpu.A)
assertEquals(0, cpu.X)
assertEquals(0, cpu.Y)
assertEquals(0, cpu.currentOpcode)
assertEquals(Cpu6502.StatusRegister(C = false, Z = false, I = true, D = false, B = false, V = false, N = false), cpu.Status)
assertEquals(0b00100100, cpu.Status.asByte())
}
@Test
fun testCpuPerformance6502() {
val cpu = Cpu6502(true) val cpu = Cpu6502(true)
val ram = Ram(0x1000, 0x2000) val ram = Ram(0x1000, 0x2000)
// load a simple program that loops a few instructions // load a simple program that loops a few instructions
@ -58,14 +77,46 @@ class Test6502CpuBasics {
} }
@Test @Test
fun testBCD() { fun testCpuPerformance65C02() {
val cpu = Cpu65C02(true)
val ram = Ram(0x0000, 0x2000)
// load a simple program that loops a few instructions
for(b in listOf(0xa9, 0x63, 0xaa, 0x86, 0x22, 0x8e, 0x22, 0x22, 0x91, 0x22, 0x6d, 0x33, 0x33, 0xcd, 0x55, 0x55,
0xff, 0xff, 0x79, 0x9e, 0x56, 0x34, 0xd0, 0xe8, 0xf0, 0xe6).withIndex()) {
ram[0x1000+b.index] = b.value.toShort()
}
val bus = Bus()
bus.add(cpu)
bus.add(ram)
cpu.reset()
cpu.PC = 0x1000
// warmup
while(cpu.totalCycles<5000000)
cpu.clock()
// timing
val cycles = 100000000
val duration = measureNanoTime {
while (cpu.totalCycles < cycles)
cpu.clock()
}
val seconds = duration.toDouble() / 1e9
val mhz = (cycles.toDouble() / seconds) / 1e6
println("duration $seconds sec for $cycles = $mhz Mhz")
}
@Test
fun testBCD6502() {
val cpu = Cpu6502(true) val cpu = Cpu6502(true)
val bus = Bus() val bus = Bus()
bus.add(cpu) bus.add(cpu)
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("bcdtest.bin")!! val bytes = javaClass.getResource("bcdtest6502.bin")!! // 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()
@ -79,7 +130,7 @@ class Test6502CpuBasics {
} }
if(ram[0x0400] ==0.toShort()) { if(ram[0x0400] ==0.toShort()) {
println("BCD TEST: OK!") println("BCD TEST for 6502: OK!")
} }
else { else {
val code = ram[0x0400] val code = ram[0x0400]

View File

@ -75,23 +75,23 @@ ${'$'}0050 00 brk""", result)
val cpu = Cpu65C02() val cpu = Cpu65C02()
val memory = Ram(0, 0x1000) val memory = Ram(0, 0x1000)
val source = javaClass.classLoader.getResource("disassem_wdc65c02.bin")!! val source = javaClass.classLoader.getResource("disassem_wdc65c02.bin")!!
memory.load(source, 0) memory.load(source, 0x200)
val resultLines = cpu.disassemble(memory, 0x0000, 0x0015) val resultLines = cpu.disassemble(memory, 0x0200, 0x0215)
val result = resultLines.joinToString("\n") val result = resultLines.joinToString("\n")
assertEquals("""${'$'}0000 cb wai assertEquals("""${'$'}0200 cb wai
${'$'}0001 db stp ${'$'}0201 db stp
${'$'}0002 3a dec a ${'$'}0202 3a dec a
${'$'}0003 1a inc a ${'$'}0203 1a inc a
${'$'}0004 64 12 stz ${'$'}12 ${'$'}0204 64 12 stz ${'$'}12
${'$'}0006 14 12 trb ${'$'}12 ${'$'}0206 14 12 trb ${'$'}12
${'$'}0008 04 12 tsb ${'$'}12 ${'$'}0208 04 12 tsb ${'$'}12
${'$'}000a 72 12 adc ${'$'}(12) ${'$'}020a 72 12 adc ${'$'}(12)
${'$'}000c b2 12 lda ${'$'}(12) ${'$'}020c b2 12 lda ${'$'}(12)
${'$'}000e 92 12 sta ${'$'}(12) ${'$'}020e 92 12 sta ${'$'}(12)
${'$'}0010 7c 00 20 jmp ${'$'}(2000,x) ${'$'}0210 7c 00 20 jmp ${'$'}(2000,x)
${'$'}0013 00 brk ${'$'}0213 00 brk
${'$'}0014 00 brk ${'$'}0214 00 brk
${'$'}0015 00 brk""", result) ${'$'}0215 00 brk""", result)
} }
} }