4

为了好玩,我正在实现一个 NES 模拟器。我目前正在阅读 6502 CPU 的文档,我有点困惑。

我看过文档说明因为 6502 是 little-endian,所以在使用绝对寻址模式时,您需要交换字节。我是在 x86 机器上编写的,它也是 little-endian,所以我不明白为什么我不能简单地转换为 uint16_t*,取消引用它,然后让编译器计算出细节。

我在谷歌测试中写了一些简单的测试,他们似乎同意我的看法。

// implementation of READ16
#define READ16(addr) (*(uint16_t*)addr)

TEST(MemMacro, READ16) {
  uint8_t arr[] = {0xFF,0xCC};
  uint8_t *mem = (&arr[0]);

  EXPECT_EQ(0xCCFF, READ16(mem));
}

这通过了,所以看来我的假设是正确的,但我想我会问比我更有经验的人。

这对于在 6502 绝对寻址模式下提取操作数是否正确?我可能错过了什么吗?

4

1 回答 1

5

它适用于 little-endian 系统上的简单案例,但是当相应的可移植实现很简单时,将您的实现与那些联系起来感觉没有必要。坚持宏,你可以这样做:

#define READ16(addr) (addr[0] + (addr[1] << 8))

(只是为了迂腐,您还应该确保它addr[1]不能越界,如果addr可能是一个复杂的表达式,则需要添加更多括号。)

但是,随着您不断开发仿真器,您会发现使用一对对单个字节进行操作的通用函数read_mem()和函数是最自然的。write_mem()请记住,地址空间被分割成多个区域(RAM、ROM 和来自 PPU 和 APU 的内存映射寄存器),因此使用索引到的单个数组将无法正常工作。映射器可以重新映射内存区域这一事实也使事情变得复杂。(不过,对于简单的游戏,你不必担心这一点——我建议从大金刚开始。)

您需要做的是找出地址在您的read_mem()write_mem()函数内部属于哪个区域或内存映射寄存器(这称为地址解码),并为地址做正确的事情。

回到最初的问题,无论如何您最终都会使用read_mem()读取地址的各个字节这一事实意味着uint16_t强制转换技巧更不可能有用。这是处理极端情况的最简单和最强大的方法,也是我在实践中看到的每个模拟器(Nestopia、Nintendulator 和 FCEUX)所做的。

如果您错过了,EFNet 上的#nesdev频道非常活跃,顺便说一句,这是一个很好的资源。我假设您已经熟悉 NESDev wiki。:)

我也一直在研究可以在这里找到的模拟器。

于 2015-04-27T03:16:32.360 回答