为了好玩,我正在实现NES模拟器。我目前正在阅读有关6502 CPU的文档,我有些困惑。
我已经看到文档说明,因为6502是低位优先的,因此在使用绝对寻址模式时,您需要交换字节。我是在x86机上写的,它也是低位字节序的,所以我不明白为什么我不能简单地转换为uint16_t *,取消引用,然后让编译器确定细节。
我已经在Google测试中写了一些简单的测试,他们似乎同意我的观点。
// 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));
}
Run Code Online (Sandbox Code Playgroud)
这通过了,所以看来我的假设是正确的,但是我想我会问比我有更多经验的人。
这对于在6502绝对寻址模式下提取操作数是否正确?我可能会错过什么吗?
它适用于小端系统上的简单情况,但是当相应的可移植实现很简单时,将实现与这些实现绑定是不必要的。坚持宏,您可以改为执行以下操作:
#define READ16(addr) (addr[0] + (addr[1] << 8))
Run Code Online (Sandbox Code Playgroud)
(只是为了学究,还应确保它addr[1]不能超出范围,并且如果addr可能是一个复杂的表达式,则需要添加更多的括号。)
但是,随着您不断开发仿真器,您会发现使用一对在单个字节上运行的通用函数read_mem()和write_mem()功能是最自然的。请记住,地址空间被分成多个区域(PPU和APU中的RAM,ROM和内存映射寄存器),因此,例如,将单个数组索引到您将无法正常工作。映射器可以重新映射内存区域的事实也使事情变得复杂。(不过,对于简单的游戏,您不必为此担心-我建议从Donkey Kong开始。)
您需要做的是弄清楚该地址属于您read_mem()和write_mem()函数内部的哪个区域或内存映射寄存器(这称为地址解码),然后对该地址执行正确的操作。
回到最初的问题,read_mem()无论如何您最终都会使用该地址来读取地址的各个字节这一事实意味着,uint16_t铸造骗局的用处不大。这是处理极端情况的最简单,最可靠的方法,也是我在实践中看到的每个模拟器(Nestopia,Nintendulator和FCEUX)所做的事情。
如果您错过了它,那么EFNet上的#nesdev频道非常活跃,并且是不错的资源。我假设您已经熟悉NESDev Wiki。:)
我也一直在研究可以在这里找到的仿真器。
| 归档时间: |
|
| 查看次数: |
880 次 |
| 最近记录: |