硬件仿真的正确实现是什么?

Asy*_*syx 2 c optimization emulation

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

第一个是C是在这里选择的编程语言。这并不是什么大问题,但我想听听您今天的观点。不推荐使用C ++。

我发现的第二件事是每个人每个操作码都使用一个函数。这似乎是合乎逻辑的,因为它只是一个函数调用,而且可能比对“ ADD”指令具有一个函数的优化效果更好,然后您必须找出此处使用的寄存器。但是今天那有什么必要呢?是我应该坚持还是要改写我的模拟器,如果我发现另一种可能更方便的方式只是没有切合(现在或多或少会出现现代游戏机)?

同样,重复编写“将寄存器添加到此寄存器”的功能也是一种动力。有没有一种方法可以从操作码映射或类似的东西中自动执行此操作?

Spe*_*tre 6

我大都同意WingsOfIcarus。我已经写了一些仿真器,所以这是我的见解:

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

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

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

    我正在使用一个包含所有说明的所有必要信息的文本文件。仿真器在初始化期间对其进行解析(提供函数指针和操作数的数组...)。在这种体系结构中,无需更改任何代码即可很容易地纠正指令集中的错误。

    复杂的指令集文档几乎总是在某种程度上存在错误。最糟糕的情况是Z80(我从未见过100%无错误的指令集)。因此,请使用更多指令集,对其进行比较,然后创建一个无错误的指令集(如果可以)。

  4. 在模拟中添加声音,视频,键盘和鼠标

    通常这不是问题。在Windows上,请使用WaveOut代替DirectSound。它更稳定,更快(DSound的可用延迟有时甚至> 400 ms)。使用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)
Run Code Online (Sandbox Code Playgroud)
  • opc 用于指针数组中的地址
  • mnemonic 用于选择适当的函数指针和操作数类型
  • T0T1用于指令计时(对于粗略的模拟就足够了)
  • MC1++ 用于正确的MC时序(以实现正确的硬件仿真和竞争时序)

这是我的Zilog Z80A完整指令集,带有机器周期计时链接供下载。随时使用(只要在某处提到我的昵称即可)。移植到此后,我终于能够100%通过ZEXALL测试。有关更多信息,请参见用C或C ++编写图形化的Z80仿真器