我正在C中编写我的第一个NES模拟器。目标是使其易于理解并且周期准确(尽管不一定必须具有代码效率),以便以正常的“硬件”速度玩游戏。当深入研究6502的技术参考时,似乎指令消耗了多个CPU周期 - 并且根据给定条件(例如分支)也有不同的周期。我的计划是创建读写函数,并使用switch
通过寻址模式对操作码进行分组。
问题是:当我有一个多周期指令时,例如BRK
,我是否需要模拟每个周期中发生的事情:
#Method 1
cycle - action
1 - read BRK opcode
2 - read padding byte (ignored)
3 - store high byte of PC
4 - store low byte of PC
5 - store status flags with B flag set
6 - low byte of target address
7 - high byte of target address
...或者我可以在一个'周期'中执行所有必需的操作(一个switch
case
)并且在剩余的周期中什么都不做?
#Method 2
1 - read BRK opcode,
read padding byte (ignored),
store high byte of PC,
store low byte of PC,
store status flags with B flag set,
low byte of target address,
high byte of target address
2 - do nothing
3 - do nothing
4 - do nothing
5 - do nothing
6 - do nothing
7 - do nothing
由于两种方法都消耗了所需的7个周期,两者之间是否会有差异? (精度明智)
我个人认为方法1是即用型解决方案,但是我想不出一个适当,简单的方法来实现它...(请帮忙!)
答案 0 :(得分:5)
你需要吗?这取决于软件。想象一下最简单的例子:
STA ($56), Y
......恰好击中了硬件寄存器。如果你至少没有在正确的周期上写,那么你已经引入了时序缺陷。您写的寄存器将在错误的时间写入。如果它像调色板寄存器,程序员正在运行光栅效果怎么办?然后你就移动了颜色变化的地方。你已经改变了图形输出。
在实践中,聪明的程序员做的事情比那更聪明 - 例如一个人可能会使用读 - 修改 - 写操作来以精确的周期读取硬件值,修改它,然后在其他一个精确的周期将其写回。
所以我的答案是:
大多数模拟器过去常用你的方法(2)。通常情况下,他们使用90%的软件。然后有一些不起作用的情况,模拟器作者在这里提出了一个特殊情况,一个特殊情况。那些通常最终会很糟糕地进行交互,并且仿真器在其余的生命周期中支持不同的95%可用软件组合,直到有人写出更好的软件。
所以请使用方法(1)。它会导致一些本来会被破坏的软件不会如此。它还会教你更多,它肯定会消除特殊情况的任何潜在动机,因此它会让你的代码更清洁。它会慢一点,但我认为你的电脑可以处理它。
其他提示:6502只有几种寻址模式,寻址模式完全决定了时序。 This document是您完美计时所需要知道的一切。如果您想要完美的清洁度,您的开关表可以选择寻址模式和中央操作,然后退出,您可以分支寻址模式进行主要操作。
如果您打算使用vanilla read
和write
方法,这在6502上是智能的,因为每个周期都是读取或写入,所以几乎所有你需要说的,只是注意方法签名。例如,6502具有SYNC引脚,允许观察者区分普通读取和操作码读取。检查NES是否将其暴露给磁带盒,因为它经常用于暴露它以进行隐式寻呼的系统,而NES的主要识别特征是有数百种寻呼方案。
编辑:次要更新: