mirror of
https://github.com/irmen/ksim65.git
synced 2025-04-11 03:37:01 +00:00
more precise cpu speed averaging
This commit is contained in:
parent
022d15d622
commit
fd84d73bd0
@ -1,9 +1,7 @@
|
||||
import org.gradle.api.internal.plugins.UnixStartScriptGenerator
|
||||
import org.gradle.api.internal.plugins.WindowsStartScriptGenerator
|
||||
import org.jetbrains.dokka.gradle.DokkaTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.util.*
|
||||
import kotlin.math.max
|
||||
import java.util.Properties
|
||||
|
||||
|
||||
plugins {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package razorvine.c64emu
|
||||
|
||||
import razorvine.examplemachines.DebugWindow
|
||||
import kotlin.concurrent.scheduleAtFixedRate
|
||||
import razorvine.ksim65.Bus
|
||||
import razorvine.ksim65.Cpu6502
|
||||
import razorvine.ksim65.IVirtualMachine
|
||||
@ -98,9 +97,16 @@ class C64Machine(title: String) : IVirtualMachine {
|
||||
debugWindow.updateCpu(cpu, bus)
|
||||
}.start()
|
||||
|
||||
java.util.Timer("cpu-clock", true).scheduleAtFixedRate(1, 1) {
|
||||
if(!paused) {
|
||||
repeat(400) {
|
||||
// busy waiting loop, averaging cpu speed to ~1 Mhz:
|
||||
var numInstructionsteps = 600
|
||||
val targetSpeedKhz = 1000
|
||||
while (true) {
|
||||
if (paused) {
|
||||
Thread.sleep(100)
|
||||
} else {
|
||||
cpu.startSpeedMeasureInterval()
|
||||
Thread.sleep(0, 1000)
|
||||
repeat(numInstructionsteps) {
|
||||
step()
|
||||
if(vic.currentRasterLine == 255) {
|
||||
// we force an irq here ourselves rather than fully emulating the VIC-II's raster IRQ
|
||||
@ -108,6 +114,11 @@ class C64Machine(title: String) : IVirtualMachine {
|
||||
cpu.irq()
|
||||
}
|
||||
}
|
||||
val speed = cpu.measureAvgIntervalSpeedKhz()
|
||||
if (speed < targetSpeedKhz - 50)
|
||||
numInstructionsteps++
|
||||
else if (speed > targetSpeedKhz + 50)
|
||||
numInstructionsteps--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,6 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("debugger"), ActionL
|
||||
it.disabledTextColor = Color.DARK_GRAY
|
||||
it.font = Font(Font.MONOSPACED, Font.PLAIN, 12)
|
||||
}
|
||||
private val startTime = System.currentTimeMillis()
|
||||
|
||||
init {
|
||||
contentPane.layout = GridBagLayout()
|
||||
@ -300,17 +299,17 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("debugger"), ActionL
|
||||
}
|
||||
}
|
||||
|
||||
fun updateCpu(cpu2: Cpu6502, bus: Bus) {
|
||||
val state = cpu2.snapshot()
|
||||
fun updateCpu(cpu: Cpu6502, bus: Bus) {
|
||||
val state = cpu.snapshot()
|
||||
cyclesTf.text = state.cycles.toString()
|
||||
regAtf.text = cpu2.hexB(state.A)
|
||||
regXtf.text = cpu2.hexB(state.X)
|
||||
regYtf.text = cpu2.hexB(state.Y)
|
||||
regAtf.text = cpu.hexB(state.A)
|
||||
regXtf.text = cpu.hexB(state.X)
|
||||
regYtf.text = cpu.hexB(state.Y)
|
||||
regPtf.text = "NV-BDIZC\n" + state.P.asInt().toString(2).padStart(8, '0')
|
||||
regPCtf.text = cpu2.hexW(state.PC)
|
||||
regSPtf.text = cpu2.hexB(state.SP)
|
||||
regPCtf.text = cpu.hexW(state.PC)
|
||||
regSPtf.text = cpu.hexB(state.SP)
|
||||
val memory = bus.memoryComponentFor(state.PC)
|
||||
disassemTf.text = cpu2.disassembleOneInstruction(memory.data, state.PC, memory.startAddress).first.substringAfter(' ').trim()
|
||||
disassemTf.text = cpu.disassembleOneInstruction(memory.data, state.PC, memory.startAddress).first.substringAfter(' ').trim()
|
||||
val pages = vm.getZeroAndStackPages()
|
||||
if(pages.isNotEmpty()) {
|
||||
val zpLines = (0..0xff step 32).map { location ->
|
||||
@ -329,9 +328,7 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("debugger"), ActionL
|
||||
stackpageTf.text = stackLines.joinToString ("\n")
|
||||
}
|
||||
|
||||
val spentTime = System.currentTimeMillis() - startTime
|
||||
val speedKhz = state.cycles.toDouble() / spentTime
|
||||
speedKhzTf.text = "%.1f".format(speedKhz)
|
||||
speedKhzTf.text = "%.1f".format(cpu.averageSpeedKhzSinceReset)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
package razorvine.examplemachines
|
||||
|
||||
import kotlin.concurrent.scheduleAtFixedRate
|
||||
import razorvine.ksim65.Bus
|
||||
import razorvine.ksim65.Cpu6502
|
||||
import razorvine.ksim65.Version
|
||||
import razorvine.ksim65.components.*
|
||||
import razorvine.ksim65.components.Display
|
||||
import razorvine.ksim65.components.Keyboard
|
||||
import razorvine.ksim65.components.Ram
|
||||
import razorvine.ksim65.components.Rom
|
||||
import javax.swing.ImageIcon
|
||||
|
||||
/**
|
||||
@ -38,16 +40,29 @@ class EhBasicMachine(title: String) {
|
||||
hostDisplay.start()
|
||||
}
|
||||
|
||||
private fun step() {
|
||||
// step a full single instruction
|
||||
while (cpu.instrCycles > 0) bus.clock()
|
||||
bus.clock()
|
||||
while (cpu.instrCycles > 0) bus.clock()
|
||||
}
|
||||
|
||||
fun start() {
|
||||
val cpuTimer = java.util.Timer("cpu-clock", true)
|
||||
cpuTimer.scheduleAtFixedRate(1, 1) {
|
||||
if(!paused) {
|
||||
repeat(500) {
|
||||
// step a full single instruction
|
||||
while (cpu.instrCycles > 0) bus.clock()
|
||||
bus.clock()
|
||||
while (cpu.instrCycles > 0) bus.clock()
|
||||
}
|
||||
// busy waiting loop, averaging cpu speed to ~1 Mhz:
|
||||
var numInstructionsteps = 600
|
||||
val targetSpeedKhz = 1000
|
||||
while (true) {
|
||||
if (paused) {
|
||||
Thread.sleep(100)
|
||||
} else {
|
||||
cpu.startSpeedMeasureInterval()
|
||||
Thread.sleep(0, 1000)
|
||||
repeat(numInstructionsteps) { step() }
|
||||
val speed = cpu.measureAvgIntervalSpeedKhz()
|
||||
if (speed < targetSpeedKhz - 50)
|
||||
numInstructionsteps++
|
||||
else if (speed > targetSpeedKhz + 50)
|
||||
numInstructionsteps--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
package razorvine.examplemachines
|
||||
|
||||
import kotlin.concurrent.scheduleAtFixedRate
|
||||
import razorvine.ksim65.Bus
|
||||
import razorvine.ksim65.Cpu6502
|
||||
import razorvine.ksim65.IVirtualMachine
|
||||
import razorvine.ksim65.Version
|
||||
import razorvine.ksim65.components.*
|
||||
import razorvine.ksim65.components.Timer
|
||||
import java.io.File
|
||||
import javax.swing.ImageIcon
|
||||
|
||||
@ -22,9 +20,11 @@ class VirtualMachine(title: String) : IVirtualMachine {
|
||||
|
||||
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, ScreenDefs.SCREEN_HEIGHT)
|
||||
ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT
|
||||
)
|
||||
private val mouse = Mouse(0xd300, 0xd305, hostDisplay)
|
||||
private val keyboard = Keyboard(0xd400, 0xd400, hostDisplay)
|
||||
private var paused = false
|
||||
@ -32,7 +32,7 @@ class VirtualMachine(title: String) : IVirtualMachine {
|
||||
init {
|
||||
hostDisplay.iconImage = ImageIcon(javaClass.getResource("/icon.png")).image
|
||||
debugWindow.iconImage = hostDisplay.iconImage
|
||||
debugWindow.setLocation(hostDisplay.location.x+hostDisplay.width, hostDisplay.location.y)
|
||||
debugWindow.setLocation(hostDisplay.location.x + hostDisplay.width, hostDisplay.location.y)
|
||||
|
||||
ram[Cpu6502.RESET_vector] = 0x00
|
||||
ram[Cpu6502.RESET_vector + 1] = 0x10
|
||||
@ -55,7 +55,7 @@ class VirtualMachine(title: String) : IVirtualMachine {
|
||||
override fun getZeroAndStackPages(): Array<UByte> = ram.getPages(0, 2)
|
||||
|
||||
override fun loadFileInRam(file: File, loadAddress: Address?) {
|
||||
if(file.extension=="prg" && loadAddress==null)
|
||||
if (file.extension == "prg" && loadAddress == null)
|
||||
ram.loadPrg(file.inputStream())
|
||||
else
|
||||
ram.load(file.readBytes(), loadAddress!!)
|
||||
@ -76,11 +76,22 @@ class VirtualMachine(title: String) : IVirtualMachine {
|
||||
javax.swing.Timer(10) {
|
||||
debugWindow.updateCpu(cpu, bus)
|
||||
}.start()
|
||||
java.util.Timer("cpu-clock", true).scheduleAtFixedRate(1, 1) {
|
||||
if(!paused) {
|
||||
repeat(50) {
|
||||
step()
|
||||
}
|
||||
|
||||
// busy waiting loop, averaging cpu speed to ~1 Mhz:
|
||||
var numInstructionsteps = 600
|
||||
val targetSpeedKhz = 1000
|
||||
while (true) {
|
||||
if (paused) {
|
||||
Thread.sleep(100)
|
||||
} else {
|
||||
cpu.startSpeedMeasureInterval()
|
||||
Thread.sleep(0, 1000)
|
||||
repeat(numInstructionsteps) { step() }
|
||||
val speed = cpu.measureAvgIntervalSpeedKhz()
|
||||
if (speed < targetSpeedKhz - 50)
|
||||
numInstructionsteps++
|
||||
else if (speed > targetSpeedKhz + 50)
|
||||
numInstructionsteps--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,11 @@ import razorvine.ksim65.components.UByte
|
||||
open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
open val name = "6502"
|
||||
var tracing: ((state:String) -> Unit)? = null
|
||||
var totalCycles: Long = 0
|
||||
var totalCycles = 0L
|
||||
protected set
|
||||
private var speedMeasureCycles = 0L
|
||||
private var speedMeasureStart = System.nanoTime()
|
||||
private var resetTime = System.nanoTime()
|
||||
|
||||
class InstructionError(msg: String) : RuntimeException(msg)
|
||||
|
||||
@ -25,25 +28,6 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
const val resetCycles = 8
|
||||
}
|
||||
|
||||
protected enum class AddrMode {
|
||||
Imp,
|
||||
Acc,
|
||||
Imm,
|
||||
Zp,
|
||||
Zpr, // special addressing mode used by the 65C02
|
||||
ZpX,
|
||||
ZpY,
|
||||
Rel,
|
||||
Abs,
|
||||
AbsX,
|
||||
AbsY,
|
||||
Ind,
|
||||
IzX,
|
||||
IzY,
|
||||
Izp, // special addressing mode used by the 65C02
|
||||
IaX, // special addressing mode used by the 65C02
|
||||
}
|
||||
|
||||
class StatusRegister(
|
||||
var C: Boolean = false,
|
||||
var Z: Boolean = false,
|
||||
@ -88,8 +72,6 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
}
|
||||
}
|
||||
|
||||
protected class Instruction(val mnemonic: String, val mode: AddrMode, val cycles: Int)
|
||||
|
||||
class BreakpointResult(val newPC: Address?, val newOpcode: Int?)
|
||||
|
||||
class State (
|
||||
@ -102,6 +84,27 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
val cycles: Long
|
||||
)
|
||||
|
||||
protected enum class AddrMode {
|
||||
Imp,
|
||||
Acc,
|
||||
Imm,
|
||||
Zp,
|
||||
Zpr, // special addressing mode used by the 65C02
|
||||
ZpX,
|
||||
ZpY,
|
||||
Rel,
|
||||
Abs,
|
||||
AbsX,
|
||||
AbsY,
|
||||
Ind,
|
||||
IzX,
|
||||
IzY,
|
||||
Izp, // special addressing mode used by the 65C02
|
||||
IaX, // special addressing mode used by the 65C02
|
||||
}
|
||||
|
||||
protected class Instruction(val mnemonic: String, val mode: AddrMode, val cycles: Int)
|
||||
|
||||
var regA: Int = 0
|
||||
var regX: Int = 0
|
||||
var regY: Int = 0
|
||||
@ -117,6 +120,9 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
val currentMnemonic: String
|
||||
get() = currentInstruction.mnemonic
|
||||
|
||||
val averageSpeedKhzSinceReset: Double
|
||||
get() = totalCycles.toDouble() / (System.nanoTime() - resetTime) * 1_000_000
|
||||
|
||||
@Synchronized fun snapshot(): State {
|
||||
val status = StatusRegister().also { it.fromInt(regP.asInt()) }
|
||||
return State(regA.toShort(),
|
||||
@ -128,6 +134,14 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
totalCycles)
|
||||
}
|
||||
|
||||
fun startSpeedMeasureInterval() {
|
||||
speedMeasureCycles = totalCycles
|
||||
speedMeasureStart = System.nanoTime()
|
||||
}
|
||||
|
||||
fun measureAvgIntervalSpeedKhz() =
|
||||
(totalCycles-speedMeasureCycles).toDouble() / (System.nanoTime() - speedMeasureStart) * 1_000_000
|
||||
|
||||
// has an interrupt been requested?
|
||||
protected enum class Interrupt {
|
||||
IRQ,
|
||||
@ -285,21 +299,23 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
* Reset the cpu
|
||||
*/
|
||||
override fun reset() {
|
||||
regP.I = true
|
||||
regP.C = false
|
||||
regP.Z = false
|
||||
regP.D = false
|
||||
regP.B = false
|
||||
regP.V = false
|
||||
regP.N = false
|
||||
regSP = 0xfd
|
||||
regPC = readWord(RESET_vector)
|
||||
regA = 0
|
||||
regX = 0
|
||||
regY = 0
|
||||
regP.C = false
|
||||
regP.Z = false
|
||||
regP.I = true
|
||||
regP.D = false
|
||||
regP.B = false
|
||||
regP.V = false
|
||||
regP.N = false
|
||||
instrCycles = resetCycles // a reset takes time as well
|
||||
currentOpcode = 0
|
||||
currentInstruction = instructions[0]
|
||||
totalCycles = 0
|
||||
resetTime = System.nanoTime()
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user