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
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
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)
[![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)*
@ -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)
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).
On my machine the library can simulate a 6502 running at up to ~100Mhz.
Properties of this simulator:
- 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() {
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
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()
}
} catch (ix: Cpu6502.InstructionError) {
println("HMMM $ix")
// ignore
println("Hmmm... $ix")
}
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)
fun disassemble(memory: Array<UByte>, baseAddress: Address, from: Address, to: Address): List<String> {
var address = from - baseAddress
var location = from - baseAddress
val spacing1 = " "
val spacing2 = " "
val spacing3 = " "
val result = mutableListOf<String>()
while (address <= (to - baseAddress)) {
val byte = memory[address]
var line = "\$${hexW(address)} ${hexB(byte)} "
address++
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 -> {
@ -152,83 +152,83 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
line += "$spacing1 ${opcode.mnemonic}"
}
AddrMode.Imm -> {
val value = memory[address++]
val value = memory[location++]
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}"
}
AddrMode.Zp -> {
val zpAddr = memory[address++]
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[address++]
val rel = memory[address++]
val zpAddr = memory[location++]
val rel = memory[location++]
val target =
if (rel <= 0x7f)
address + rel
location + rel + baseAddress
else
address - (256 - rel)
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[address++]
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[address++]
val hi = memory[address++]
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[address++]
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x"
}
AddrMode.ZpY -> {
val zpAddr = memory[address++]
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y"
}
AddrMode.Rel -> {
val rel = memory[address++]
val rel = memory[location++]
val target =
if (rel <= 0x7f)
address + rel
location + rel + baseAddress
else
address - (256 - rel)
location - (256 - rel) + baseAddress
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}"
}
AddrMode.Abs -> {
val lo = memory[address++]
val hi = memory[address++]
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[address++]
val hi = memory[address++]
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[address++]
val hi = memory[address++]
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[address++]
val hi = memory[address++]
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[address++]
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)"
}
AddrMode.IzY -> {
val zpAddr = memory[address++]
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y"
}
}

View File

@ -1,5 +1,6 @@
import razorvine.ksim65.components.Bus
import razorvine.ksim65.components.Cpu6502
import razorvine.ksim65.components.Cpu65C02
import razorvine.ksim65.components.Ram
import kotlin.test.*
import kotlin.system.measureNanoTime
@ -9,7 +10,7 @@ import kotlin.test.assertEquals
class Test6502CpuBasics {
@Test
fun testCpuFlagsAfterReset() {
fun testCpuFlagsAfterReset6502() {
val cpu = Cpu6502(true)
val bus = Bus()
bus.add(cpu)
@ -27,7 +28,25 @@ class Test6502CpuBasics {
}
@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 ram = Ram(0x1000, 0x2000)
// load a simple program that loops a few instructions
@ -58,14 +77,46 @@ class Test6502CpuBasics {
}
@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 bus = Bus()
bus.add(cpu)
val ram = Ram(0, 0xffff)
ram[Cpu6502.RESET_vector] = 0x00
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)
bus.add(ram)
bus.reset()
@ -79,7 +130,7 @@ class Test6502CpuBasics {
}
if(ram[0x0400] ==0.toShort()) {
println("BCD TEST: OK!")
println("BCD TEST for 6502: OK!")
}
else {
val code = ram[0x0400]

View File

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