CPU仿真并将其锁定为特定的时钟速度
如果我们读过我的其他问题,我们会知道我这个周末已经花了6502 CPU仿真器作为编程练习。
CPU仿真器大体上是完整的,并且从我有限的测试来看似乎相当准确,但是它的运行速度非常快,因此我希望将其降低到机器的实际时钟速度。
我当前的测试循环是这样的:
// Just loop infinitely. while (1 == 1) { CPU.ClockCyclesBeforeNext--; if (CPU.ClockCyclesBeforeNext <= 0) { // Find out how many clock cycles this instruction will take CPU.ClockCyclesBeforeNext = CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].CpuCycles; // Run the instruction CPU.ExecuteInstruction(CPU.Memory[CPU.PC]); // Debugging Info CPU.DumpDebug(); Console.WriteLine(CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].ArgumentLength); // Move to next instruction CPU.PC += 1 + CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].ArgumentLength; } }
如我们所知,每个操作码都需要花费一定的时间才能完成,因此在我倒计时CPU周期时钟之前,我不会运行下一条指令。这在操作码之间提供了适当的时间,只是整个过程运行得很快。
目标CPU速度为1.79mhz,但是我希望通过任何解决时钟问题的方法将速度保持在1.79mhz,即使我添加了复杂性,因此也不必进行调整。
有任何想法吗?
解决方案
我将使用时钟周期来计算时间,并且它们会睡在时间上的差异。当然,要做到这一点,我们需要一个高分辨率时钟。他们这样做的方式会使CPU陷入旋转循环。
查看原始的QuickTime文档以获取灵感。
它是很久以前写的,当时显示视频意味着仅以足够高的速度交换静止帧,但苹果公司决定他们需要一个完整的时间管理框架。最初的设计看起来过于工程化,但它可以使它们处理速度差异很大的要求,并使它们保持紧密同步。
我们幸运的是6502具有确定的时间行为,每条指令花费的确切时间都有详细记录;但这不是恒定的。有些指令需要2个周期,其他指令需要3个周期。就像QuickTime中的帧一样,视频没有"每秒帧数"参数,每个帧都告诉它要在屏幕上显示多长时间。
由于现代CPU的不确定性很高,并且多任务OS甚至可能冻结几毫秒(虚拟内存!),因此,如果我们不按计划进行,或者我们可以小睡几微秒,则应该保留一个标签。
多年前,我写了一个Z80仿真器,并且为了精确执行周期,我将时钟速率划分为多个小块,并让内核执行了那么多时钟周期。就我而言,我将其与我正在模拟的游戏系统的帧速率绑定在一起。每个操作码都知道要执行多少个周期,内核将继续运行操作码,直到执行了指定数量的周期为止。我有一个外部运行循环,它将运行cpu核心,并运行仿真系统的其他部分,然后休眠直到下一次迭代的开始时间。
编辑:添加示例的运行循环。
int execute_run_loop( int cycles ) { int n = 0; while( n < cycles ) { /* Returns number of cycles executed */ n += execute_next_opcode(); } return n; }
希望这可以帮助。
正如jfk所说,最常用的方法是将CPU速度与(模拟)视频输出的垂直刷新联系起来。
选择每个视频帧要运行的周期数。这通常是特定于机器的,但是我们可以通过以下方式进行计算:
cycles = clock speed in Hz / required frames-per-second
然后,我们也要入睡,直到点击视频更新为止,这时我们将开始接下来的n个CPU仿真周期。
如果我们要特别模拟某些东西,则只需查找fps速率和处理器速度即可大致正确。
编辑:如果我们没有任何外部时序要求,那么模拟器尽可能快地运行是正常的。有时这是理想的效果,有时不是:)