1
0
mirror of https://github.com/irmen/ksim65.git synced 2024-05-29 03:41:30 +00:00

more precise cpu speed averaging

This commit is contained in:
Irmen de Jong 2019-09-27 22:38:36 +02:00
parent 022d15d622
commit fd84d73bd0
6 changed files with 118 additions and 70 deletions

View File

@ -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 {

View File

@ -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--
}
}
}

View File

@ -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)
}
}

View File

@ -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--
}
}
}

View File

@ -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--
}
}
}

View File

@ -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()
}
/**