mirror of
https://github.com/irmen/ksim65.git
synced 2024-05-29 03:41:30 +00:00
cleanups
This commit is contained in:
parent
66c4033eb4
commit
84831adb07
|
@ -1,5 +1,6 @@
|
|||
import org.jetbrains.dokka.gradle.DokkaTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import kotlin.math.max
|
||||
|
||||
plugins {
|
||||
// Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
|
||||
|
@ -46,9 +47,9 @@ tasks.named<Test>("test") {
|
|||
testLogging.events("failed")
|
||||
|
||||
// parallel tests.
|
||||
systemProperties["junit.jupiter.execution.parallel.enabled"] = true
|
||||
systemProperties["junit.jupiter.execution.parallel.mode.default"] = "concurrent"
|
||||
maxParallelForks = Runtime.getRuntime().availableProcessors() / 2
|
||||
systemProperty("junit.jupiter.execution.parallel.enabled", "true")
|
||||
systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent")
|
||||
maxParallelForks = max(1, Runtime.getRuntime().availableProcessors() / 2)
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().all {
|
||||
|
|
|
@ -65,13 +65,14 @@ private fun startSimulator(args: Array<String>) {
|
|||
|
||||
cpu.Status.I = false // enable interrupts
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
bus.clock()
|
||||
}
|
||||
} catch (ix: Cpu6502.InstructionError) {
|
||||
println("Hmmm... $ix")
|
||||
}
|
||||
// TODO
|
||||
// try {
|
||||
// while (true) {
|
||||
// bus.clock()
|
||||
// }
|
||||
// } catch (ix: Cpu6502.InstructionError) {
|
||||
// println("Hmmm... $ix")
|
||||
// }
|
||||
|
||||
ram.hexDump(0x1000, 0x1020)
|
||||
val dis = cpu.disassemble(ram, 0x1000, 0x1020)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,5 @@
|
|||
package razorvine.ksim65.components
|
||||
|
||||
import razorvine.ksim65.c64.Petscii
|
||||
|
||||
typealias UByte = Short
|
||||
typealias Address = Int
|
||||
|
||||
|
@ -22,7 +20,7 @@ abstract class MemMappedComponent(val startAddress: Address, val endAddress: Add
|
|||
require(startAddress >= 0 && endAddress <= 0xffff) { "can only have 16-bit address space" }
|
||||
}
|
||||
|
||||
fun hexDump(from: Address, to: Address) {
|
||||
fun hexDump(from: Address, to: Address, charmapper: ((Short)->Char)? = null) {
|
||||
(from..to).chunked(16).forEach {
|
||||
print("\$${it.first().toString(16).padStart(4, '0')} ")
|
||||
val bytes = it.map { address -> get(address) }
|
||||
|
@ -30,8 +28,12 @@ abstract class MemMappedComponent(val startAddress: Address, val endAddress: Add
|
|||
print(byte.toString(16).padStart(2, '0') + " ")
|
||||
}
|
||||
print(" ")
|
||||
print(Petscii.decodeScreencode(bytes, false).replace('\ufffe', '.'))
|
||||
println()
|
||||
val chars =
|
||||
if(charmapper!=null)
|
||||
bytes.map { b -> charmapper(b) }
|
||||
else
|
||||
bytes.map { b -> if(b in 32..255) b.toChar() else '.' }
|
||||
println(chars.joinToString(""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ package razorvine.ksim65.components
|
|||
* TODO: add the optional additional cycles to certain instructions and addressing modes
|
||||
*/
|
||||
open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
var tracing: Boolean = false
|
||||
var tracing: ((state:String) -> Unit)? = null
|
||||
var totalCycles: Long = 0
|
||||
protected set
|
||||
|
||||
|
@ -20,7 +20,7 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
const val resetCycles = 8
|
||||
}
|
||||
|
||||
enum class AddrMode {
|
||||
protected enum class AddrMode {
|
||||
Imp,
|
||||
Acc,
|
||||
Imm,
|
||||
|
@ -39,8 +39,6 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
IaX, // special addressing mode used by the 65C02
|
||||
}
|
||||
|
||||
class Instruction(val mnemonic: String, val mode: AddrMode, val cycles: Int)
|
||||
|
||||
class StatusRegister(
|
||||
var C: Boolean = false,
|
||||
var Z: Boolean = false,
|
||||
|
@ -85,8 +83,9 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
}
|
||||
}
|
||||
|
||||
protected class Instruction(val mnemonic: String, val mode: AddrMode, val cycles: Int)
|
||||
|
||||
|
||||
var instrCycles: Int = 0
|
||||
var A: Int = 0
|
||||
var X: Int = 0
|
||||
var Y: Int = 0
|
||||
|
@ -94,24 +93,32 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
var PC: Address = 0
|
||||
val Status = StatusRegister()
|
||||
var currentOpcode: Int = 0
|
||||
protected set
|
||||
var instrCycles: Int = 0
|
||||
protected set
|
||||
|
||||
protected lateinit var currentInstruction: Instruction
|
||||
|
||||
// has an interrupt been requested?
|
||||
protected var pendingInterrupt: Pair<Boolean, BusComponent>? = null
|
||||
|
||||
// data byte from the instruction (only set when addr.mode is Accumulator, Immediate or Implied)
|
||||
private var fetchedData: Int = 0
|
||||
protected var fetchedData: Int = 0
|
||||
|
||||
// all other addressing modes yield a fetched memory address
|
||||
protected var fetchedAddress: Address = 0
|
||||
|
||||
private val breakpoints = mutableMapOf<Address, (cpu: Cpu6502, pc: Address) -> Unit>()
|
||||
class BreakpointResult(val newPC: Address?, val newOpcode: Int?)
|
||||
|
||||
fun breakpoint(address: Address, action: (cpu: Cpu6502, pc: Address) -> Unit) {
|
||||
private val breakpoints = mutableMapOf<Address, (cpu: Cpu6502, pc: Address) -> BreakpointResult>()
|
||||
|
||||
fun addBreakpoint(address: Address, action: (cpu: Cpu6502, pc: Address) -> BreakpointResult) {
|
||||
breakpoints[address] = action
|
||||
}
|
||||
|
||||
internal fun hexW(number: Address, allowSingleByte: Boolean = false): String {
|
||||
fun removeBreakpoint(address: Address) = breakpoints.remove(address)
|
||||
|
||||
fun hexW(number: Address, allowSingleByte: Boolean = false): String {
|
||||
val msb = number ushr 8
|
||||
val lsb = number and 0xff
|
||||
return if (msb == 0 && allowSingleByte)
|
||||
|
@ -120,9 +127,9 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
hexB(msb) + hexB(lsb)
|
||||
}
|
||||
|
||||
internal fun hexB(number: Short): String = hexB(number.toInt())
|
||||
fun hexB(number: Short): String = hexB(number.toInt())
|
||||
|
||||
internal fun hexB(number: Int): String {
|
||||
fun hexB(number: Int): String {
|
||||
val hexdigits = "0123456789abcdef"
|
||||
val loNibble = number and 15
|
||||
val hiNibble = number ushr 4
|
||||
|
@ -268,16 +275,19 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
currentOpcode = read(PC)
|
||||
currentInstruction = instructions[currentOpcode]
|
||||
|
||||
if (tracing) printState()
|
||||
tracing?.invoke(logState())
|
||||
|
||||
breakpoints[PC]?.let {
|
||||
breakpoints[PC]?.let { breakpoint ->
|
||||
val oldPC = PC
|
||||
val oldOpcode = currentOpcode
|
||||
it(this, PC)
|
||||
val result = breakpoint(this, PC)
|
||||
if(result.newPC!=null)
|
||||
PC = result.newPC
|
||||
if (PC != oldPC)
|
||||
return clock()
|
||||
if (oldOpcode != currentOpcode)
|
||||
else if(result.newOpcode!=null) {
|
||||
currentOpcode = result.newOpcode
|
||||
currentInstruction = instructions[currentOpcode]
|
||||
}
|
||||
}
|
||||
|
||||
if (stopOnBrk && currentOpcode == 0) {
|
||||
|
@ -311,9 +321,8 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
pendingInterrupt = Pair(false, source)
|
||||
}
|
||||
|
||||
fun printState() {
|
||||
println(
|
||||
"cycle:$totalCycles - pc=${hexW(PC)} " +
|
||||
fun logState(): String =
|
||||
"cycle:$totalCycles - pc=${hexW(PC)} " +
|
||||
"A=${hexB(A)} " +
|
||||
"X=${hexB(X)} " +
|
||||
"Y=${hexB(Y)} " +
|
||||
|
@ -326,8 +335,6 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
" z=" + (if (Status.Z) "1" else "0") +
|
||||
" c=" + (if (Status.C) "1" else "0") +
|
||||
" icycles=$instrCycles instr=${hexB(currentOpcode)}:${currentInstruction.mnemonic}"
|
||||
)
|
||||
}
|
||||
|
||||
protected fun getFetched() =
|
||||
if (currentInstruction.mode == AddrMode.Imm ||
|
||||
|
@ -361,7 +368,7 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
|||
return read(SP or 0x0100)
|
||||
}
|
||||
|
||||
private fun popStackAddr(): Address {
|
||||
protected fun popStackAddr(): Address {
|
||||
val lo = popStack()
|
||||
val hi = popStack()
|
||||
return lo or (hi shl 8)
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package razorvine.ksim65.components
|
||||
|
||||
import razorvine.ksim65.c64.Petscii
|
||||
|
||||
/**
|
||||
* A parallel output device (basically, prints bytes as characters to the screen)
|
||||
* First address = data byte (8 parallel bits)
|
||||
* Second address = control byte (bit 0 high = write byte)
|
||||
*/
|
||||
class ParallelPort(startAddress: Address, endAddress: Address) : MemMappedComponent(startAddress, endAddress) {
|
||||
var dataByte: UByte = 0
|
||||
private var dataByte: UByte = 0
|
||||
|
||||
init {
|
||||
require(endAddress - startAddress + 1 == 2) { "parallel needs exactly 2 memory bytes (data + control)" }
|
||||
|
@ -29,7 +27,7 @@ class ParallelPort(startAddress: Address, endAddress: Address) : MemMappedCompon
|
|||
dataByte = data
|
||||
else if (address == endAddress) {
|
||||
if ((data.toInt() and 1) == 1) {
|
||||
val char = Petscii.decodeScreencode(listOf(dataByte), false).first()
|
||||
val char = dataByte.toChar()
|
||||
println("PARALLEL WRITE: '$char'")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import razorvine.ksim65.c64.Petscii
|
||||
import razorvine.ksim65.components.Address
|
||||
import razorvine.ksim65.components.Cpu6502
|
||||
import razorvine.ksim65.components.Ram
|
||||
|
@ -6,17 +5,17 @@ import razorvine.ksim65.components.Ram
|
|||
|
||||
class C64KernalStubs(private val ram: Ram) {
|
||||
|
||||
fun handleBreakpoint(cpu: Cpu6502, pc: Address) {
|
||||
fun handleBreakpoint(cpu: Cpu6502, pc: Address): Cpu6502.BreakpointResult {
|
||||
when(pc) {
|
||||
0xffd2 -> {
|
||||
// CHROUT
|
||||
ram[0x030c] = 0
|
||||
val char = Petscii.decodePetscii(listOf(cpu.A.toShort()), true).first()
|
||||
val char = cpu.A.toChar()
|
||||
if(char==13.toChar())
|
||||
println()
|
||||
else if(char in ' '..'~')
|
||||
print(char)
|
||||
cpu.currentOpcode = 0x60 // rts to end the stub
|
||||
return Cpu6502.BreakpointResult(null, 0x60) // perform an RTS to exit this subroutine
|
||||
}
|
||||
0xffe4 -> {
|
||||
// GETIN
|
||||
|
@ -40,6 +39,8 @@ class C64KernalStubs(private val ram: Ram) {
|
|||
// cpu.PC = 0x0816 // continue in next module
|
||||
}
|
||||
}
|
||||
|
||||
return Cpu6502.BreakpointResult(null, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,10 +20,11 @@ class Test6502Functional {
|
|||
bus.add(ram)
|
||||
cpu.reset()
|
||||
cpu.PC = 0x0400
|
||||
cpu.breakpoint(0x3469) { _, _ ->
|
||||
cpu.addBreakpoint(0x3469) { _, _ ->
|
||||
// reaching this address means successful test result
|
||||
if(cpu.currentOpcode==0x4c)
|
||||
throw SuccessfulTestResult()
|
||||
Cpu6502.BreakpointResult(null, null)
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -35,7 +36,7 @@ class Test6502Functional {
|
|||
return
|
||||
}
|
||||
|
||||
cpu.printState()
|
||||
println(cpu.logState())
|
||||
val d = cpu.disassemble(ram, cpu.PC-20, cpu.PC+20)
|
||||
println(d.joinToString ("\n"))
|
||||
fail("test failed")
|
||||
|
@ -51,10 +52,11 @@ class Test6502Functional {
|
|||
bus.add(ram)
|
||||
cpu.reset()
|
||||
cpu.PC = 0x0400
|
||||
cpu.breakpoint(0x24f1) { _, _ ->
|
||||
cpu.addBreakpoint(0x24f1) { _, _ ->
|
||||
// reaching this address means successful test result
|
||||
if(cpu.currentOpcode==0x4c)
|
||||
throw SuccessfulTestResult()
|
||||
Cpu6502.BreakpointResult(null, null)
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -66,7 +68,7 @@ class Test6502Functional {
|
|||
return
|
||||
}
|
||||
|
||||
cpu.printState()
|
||||
println(cpu.logState())
|
||||
val d = cpu.disassemble(ram, cpu.PC-20, cpu.PC+20)
|
||||
println(d.joinToString ("\n"))
|
||||
fail("test failed")
|
||||
|
|
|
@ -7,7 +7,7 @@ import kotlin.test.*
|
|||
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
|
||||
@Disabled("this test suite takes a long time")
|
||||
// @Disabled("this test suite takes a long time")
|
||||
class Test6502TestSuite {
|
||||
|
||||
val cpu: Cpu6502 = Cpu6502(stopOnBrk = false)
|
||||
|
@ -16,10 +16,10 @@ class Test6502TestSuite {
|
|||
val kernalStubs = C64KernalStubs(ram)
|
||||
|
||||
init {
|
||||
cpu.tracing = false
|
||||
cpu.breakpoint(0xffd2) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
|
||||
cpu.breakpoint(0xffe4) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
|
||||
cpu.breakpoint(0xe16f) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
|
||||
cpu.tracing = null
|
||||
cpu.addBreakpoint(0xffd2) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
|
||||
cpu.addBreakpoint(0xffe4) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
|
||||
cpu.addBreakpoint(0xe16f) { cpu, pc -> kernalStubs.handleBreakpoint(cpu, pc) }
|
||||
|
||||
// create the system bus and add device to it.
|
||||
// note that the order is relevant w.r.t. where reads and writes are going.
|
||||
|
@ -54,7 +54,7 @@ class Test6502TestSuite {
|
|||
while (cpu.totalCycles < 50000000L) {
|
||||
bus.clock()
|
||||
}
|
||||
fail("test hangs")
|
||||
fail("test hangs: " + cpu.logState())
|
||||
} catch (e: Cpu6502.InstructionError) {
|
||||
println(">>> INSTRUCTION ERROR: ${e.message}")
|
||||
} catch (le: KernalLoadNextPart) {
|
||||
|
|
|
@ -30,44 +30,44 @@ class TestDisassembler {
|
|||
val cpu = Cpu65C02()
|
||||
val memory = Ram(0, 0x1000)
|
||||
val source = javaClass.classLoader.getResource("disassem_r65c02.bin")!!
|
||||
memory.load(source, 0)
|
||||
val resultLines = cpu.disassemble(memory, 0x0000, 0x0050)
|
||||
memory.load(source, 0x0200)
|
||||
val resultLines = cpu.disassemble(memory, 0x0200, 0x0250)
|
||||
val result = resultLines.joinToString("\n")
|
||||
assertEquals("""${'$'}0000 07 12 rmb0 ${'$'}12
|
||||
${'$'}0002 17 12 rmb1 ${'$'}12
|
||||
${'$'}0004 27 12 rmb2 ${'$'}12
|
||||
${'$'}0006 37 12 rmb3 ${'$'}12
|
||||
${'$'}0008 47 12 rmb4 ${'$'}12
|
||||
${'$'}000a 57 12 rmb5 ${'$'}12
|
||||
${'$'}000c 67 12 rmb6 ${'$'}12
|
||||
${'$'}000e 77 12 rmb7 ${'$'}12
|
||||
${'$'}0010 87 12 smb0 ${'$'}12
|
||||
${'$'}0012 97 12 smb1 ${'$'}12
|
||||
${'$'}0014 a7 12 smb2 ${'$'}12
|
||||
${'$'}0016 b7 12 smb3 ${'$'}12
|
||||
${'$'}0018 c7 12 smb4 ${'$'}12
|
||||
${'$'}001a d7 12 smb5 ${'$'}12
|
||||
${'$'}001c e7 12 smb6 ${'$'}12
|
||||
${'$'}001e f7 12 smb7 ${'$'}12
|
||||
${'$'}0020 0f 12 2a bbr0 ${'$'}12, ${'$'}4d
|
||||
${'$'}0023 1f 12 27 bbr1 ${'$'}12, ${'$'}4d
|
||||
${'$'}0026 2f 12 24 bbr2 ${'$'}12, ${'$'}4d
|
||||
${'$'}0029 3f 12 21 bbr3 ${'$'}12, ${'$'}4d
|
||||
${'$'}002c 4f 12 1e bbr4 ${'$'}12, ${'$'}4d
|
||||
${'$'}002f 5f 12 1b bbr5 ${'$'}12, ${'$'}4d
|
||||
${'$'}0032 6f 12 18 bbr6 ${'$'}12, ${'$'}4d
|
||||
${'$'}0035 8f 12 15 bbs0 ${'$'}12, ${'$'}4d
|
||||
${'$'}0038 9f 12 12 bbs1 ${'$'}12, ${'$'}4d
|
||||
${'$'}003b af 12 0f bbs2 ${'$'}12, ${'$'}4d
|
||||
${'$'}003e bf 12 0c bbs3 ${'$'}12, ${'$'}4d
|
||||
${'$'}0041 cf 12 09 bbs4 ${'$'}12, ${'$'}4d
|
||||
${'$'}0044 df 12 06 bbs5 ${'$'}12, ${'$'}4d
|
||||
${'$'}0047 ef 12 03 bbs6 ${'$'}12, ${'$'}4d
|
||||
${'$'}004a ff 12 00 bbs7 ${'$'}12, ${'$'}4d
|
||||
${'$'}004d 00 brk
|
||||
${'$'}004e 00 brk
|
||||
${'$'}004f 00 brk
|
||||
${'$'}0050 00 brk""", result)
|
||||
assertEquals("""${'$'}0200 07 12 rmb0 ${'$'}12
|
||||
${'$'}0202 17 12 rmb1 ${'$'}12
|
||||
${'$'}0204 27 12 rmb2 ${'$'}12
|
||||
${'$'}0206 37 12 rmb3 ${'$'}12
|
||||
${'$'}0208 47 12 rmb4 ${'$'}12
|
||||
${'$'}020a 57 12 rmb5 ${'$'}12
|
||||
${'$'}020c 67 12 rmb6 ${'$'}12
|
||||
${'$'}020e 77 12 rmb7 ${'$'}12
|
||||
${'$'}0210 87 12 smb0 ${'$'}12
|
||||
${'$'}0212 97 12 smb1 ${'$'}12
|
||||
${'$'}0214 a7 12 smb2 ${'$'}12
|
||||
${'$'}0216 b7 12 smb3 ${'$'}12
|
||||
${'$'}0218 c7 12 smb4 ${'$'}12
|
||||
${'$'}021a d7 12 smb5 ${'$'}12
|
||||
${'$'}021c e7 12 smb6 ${'$'}12
|
||||
${'$'}021e f7 12 smb7 ${'$'}12
|
||||
${'$'}0220 0f 12 2a bbr0 ${'$'}12, ${'$'}024d
|
||||
${'$'}0223 1f 12 27 bbr1 ${'$'}12, ${'$'}024d
|
||||
${'$'}0226 2f 12 24 bbr2 ${'$'}12, ${'$'}024d
|
||||
${'$'}0229 3f 12 21 bbr3 ${'$'}12, ${'$'}024d
|
||||
${'$'}022c 4f 12 1e bbr4 ${'$'}12, ${'$'}024d
|
||||
${'$'}022f 5f 12 1b bbr5 ${'$'}12, ${'$'}024d
|
||||
${'$'}0232 6f 12 18 bbr6 ${'$'}12, ${'$'}024d
|
||||
${'$'}0235 8f 12 15 bbs0 ${'$'}12, ${'$'}024d
|
||||
${'$'}0238 9f 12 12 bbs1 ${'$'}12, ${'$'}024d
|
||||
${'$'}023b af 12 0f bbs2 ${'$'}12, ${'$'}024d
|
||||
${'$'}023e bf 12 0c bbs3 ${'$'}12, ${'$'}024d
|
||||
${'$'}0241 cf 12 09 bbs4 ${'$'}12, ${'$'}024d
|
||||
${'$'}0244 df 12 06 bbs5 ${'$'}12, ${'$'}024d
|
||||
${'$'}0247 ef 12 03 bbs6 ${'$'}12, ${'$'}024d
|
||||
${'$'}024a ff 12 00 bbs7 ${'$'}12, ${'$'}024d
|
||||
${'$'}024d 00 brk
|
||||
${'$'}024e 00 brk
|
||||
${'$'}024f 00 brk
|
||||
${'$'}0250 00 brk""", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue
Block a user