3

我将编写一个Game Boy模拟器(Z80是 CPU,以防有人不熟悉它),在我进行研究时,我发现了一些我不太确定的东西。

第一个是 C 是这里选择的编程语言。这不是什么大问题,但我想从今天的角度听听你的意见。甚至不推荐使用 C++。

我发现的第二件事是每个操作码每个人都使用一个函数。这似乎是合乎逻辑的,因为它只是一个函数调用,并且可能比为“ADD”指令使用一个函数更好地优化,然后你必须找出这里使用了哪些寄存器。但今天这有多大必要?如果我注意到另一种可能更方便的方式并没有减少它(现在或多或少现代游戏机出现在我的脑海中),我应该坚持还是我应该重写我的模拟器?

此外,一遍又一遍地编写“将该寄存器添加到该寄存器”的函数有点令人沮丧。有没有办法从操作码映射或类似的东西自动化?

4

3 回答 3

9

我主要同意 WingsOfIcarus 的观点。我已经写了一些模拟器,所以这是我的见解:

  1. 使用函数指针是个好主意(为了代码的速度和清晰度)
  2. OOP 不是问题

    是的,成员调用有点慢,但如果你小心它不会对性能产生太大影响。另一方面,OOP 仿真代码更易于管理/阅读/理解。

  3. 使用指令数据库而不是固定的指令解码。

    我正在使用一个文本文件,其中包含所有指令的所有必要信息。模拟器在初始化期间解析它(提供函数指针和操作数的数组......)。在这种架构中,很容易纠正指令集中的错误,而无需更改任何代码。

    复杂的指令集文档在某些方面几乎总是有缺陷的。最坏的情况是Z80(我从未见过 100% 无错误的指令集)。所以使用更多的指令集,比较它们并创建一个无错误的集(如果可以的话)。

  4. 将声音、视频、键盘和鼠标添加到您的仿真中

    这通常不是问题。在Windows上使用WaveOut而不是DirectSound。它更稳定、更快(DSound 的可用延迟有时甚至 > 400 毫秒)。使用 WaveOut,我可以将延迟控制在 20-80 毫秒,这没问题。

  5. 以每秒模拟 CPU 的 T 个周期应用限制速度

    我正在使用机器周期正确的时间,这要慢得多,但允许我正确地实现任何硬件外围仿真(FDC、DMAC、声音芯片......没有任何黑客)

  6. 为模拟平台应用文件的加载/保存

例如,这是我的指令集的一部分(直接提供给 CPU 仿真:

opc      T0 T1 MC1   MC2   MC3   MC4   MC5   MC6   MC7   mnemonic

B8       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,B
B9       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,C
BA       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,D
BB       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,E
BC       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,H
BD       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,L
BE       07 00 M1R 4 MRD 3 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,(HL)
BF       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,A
C0       11 05 M1R 5 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 RET NZ
C1       10 00 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 POP BC
C2L2H2   10 10 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 JP NZ,U16
C3L1H1   10 00 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 JP U16
C4L2H2   17 10 M1R 4 MRD 3 MRD 4 MWR 3 MWR 3 ... 0 ... 0 CALL NZ,U16
C5       11 00 M1R 5 MWR 3 MWR 3 ... 0 ... 0 ... 0 ... 0 PUSH BC
C6U2     07 00 M1R 4 MRD 3 ... 0 ... 0 ... 0 ... 0 ... 0 ADD A,U8
C7       11 00 M1R 5 MWR 3 MWR 3 ... 0 ... 0 ... 0 ... 0 RST 00H
C8       11 05 M1R 5 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 RET Z
C9       10 00 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 RET
CAL2H2   10 10 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 JP Z,U16

opc:    operation code [hex]
        L1,H1,U1,S1 means first operand direct number or address
        L2,H2,U2,S2 means second operand direct number or address
        L3,H3,U3,S3 means third operand direct number or address
        H,L ... U16 high and low byte
        U   ... U8 unsigned byte
        S   ... S8 signed byte

T0      normal instruction duration [T] always 2 decimal digits
T1      instruction duration if condition not met [T] always 2 decimal digits

MC1++   Machine cycle first is type,second is duration [T] always 1 decimal digit
        ...     unused
        M1R     M1 cycle
        MRD     memory read
        MWR     memory write
        IOR     IO read
        IOW     IO write
        NON     no external operation (internal computation)
        INT     interrupt cycle

mnem    instruction text (mnemonic)
  • opc用于指针数组中的地址
  • mnemonic用于选择正确的函数指针和操作数类型
  • T0T1用于指令计时(这对于粗略的仿真来说已经足够了)
  • MC1++用于正确的 MC 时序(实现正确的硬件仿真和争用时序)

这是我的 Zilog Z80A 完整指令集,带有机器周期时序链接供下载。随意使用(只需在某处提及我的昵称)。在移植到这个之后,我终于能够 100% 通过ZEXALL测试。有关详细信息,请参阅用 C 或 C++ 编写图形 Z80 仿真器

于 2013-09-20T07:54:02.360 回答
2

第一个建议,你不应该使用嵌套的 switch 语句,你应该使用函数指针数组,更快 -> 更好的模拟,更好的代码,嵌套的 switch-es 也会有点混乱,这里有一些你可以的链接阅读有关这些数组的更多信息
http://www.newty.de/fpt/fpt.html
http://www.multigesture.net/wp-content/uploads/mirror/zenogais/FunctionPointers.htm

第二个建议,是的,您可以在 C#、Java、C++ 中执行此操作,但是由于您想要 CPU 周期的每一位,因此您可以获得尽可能接近的仿真 - 以最少的 CPU 周期模拟目标架构的一个 CPU 周期curret 架构,在这种情况下,从我从人们那里听到/读到的内容来看,OOP 并不是那么好。其中一件事是性能,第二件事非常明显,正如您可能注意到的那样,仿真是非常复杂的任务,并且将其包装在 OOP 中可能会给颈部带来不必要的痛苦。

于 2013-04-03T01:35:43.183 回答
1

这是使用 NES 模拟器的一些操作码的一个非常酷的实现:

http://bisqwit.iki.fi/jutut/kuvat/programming_examples/nesemu1/

这是随附的 youtube 视频,对正在发生的事情有更多解释

http://www.youtube.com/watch?v=y71lli8MS8s

它使用 C++ 模板和一些额外的 C++11 特性。至于您是选择 C++ 还是 C,这取决于您,但这并不重要。如果你只是在模仿一个游戏机,我怀疑速度在现代处理器上是否会成为一个问题,所以尽量只使用你喜欢的任何东西。

于 2013-03-28T23:48:32.040 回答