仿真实现CPU指令?

时间:2013-06-22 16:51:47

标签: c emulation

我正在尝试学习仿真编程。我已经完成了一个CHIP-8仿真器,40个以下的指令,并且因为我的音乐而活着。我现在希望做一些更复杂的事情,比如SNES。我遇到的问题是CPU指令的数量。透过wiki.SuperFamicom.org 65c816 instruction listing看,它看起来像是后方的痛苦。我在各种互联网页面上看到了这些和那里的注释,CPU是仿真器中最容易实现的部分。

假设它很难,因为我做错了,我环顾四周,发现了一个简单的暗示:SNES Emulator in 15 minutes这是大约900行代码。很容易完成。

然后,从SNES Emulator in 15 minutes源,我找到了CPU指令的位置。它看起来比我想的要简单得多。我真的不明白它,但它是几行代码而不是大量的代码。我注意到的第一件事是指令每个只有1个实施。如果您查看SuperFamicom中的表格,那么您会看到它有

ADC #const
ADC (_db_),X
ADC (_db_,X)
ADC addr
ADC long
...

和(我认为)所有这些的模拟器源是:

// Note: op 0x100 means "NMI", 0x101 means "Reset", 0x102 means "IRQ". They are implemented in terms of "BRK".
// User is responsible for ensuring that WB() will not store into memory while Reset is being processed.
unsigned addr=0, d=0, t=0xFF, c=0, sb=0, pbits = op<0x100 ? 0x30 : 0x20;

// Define the opcode decoding matrix, which decides which micro-operations constitute
// any particular opcode. (Note: The PLA of 6502 works on a slightly different principle.)
const unsigned o8 = op / 32, o8m = 1u << (op%32);
// Fetch op'th item from a bitstring encoded in a data-specific variant of base64,
// where each character transmits 8 bits of information rather than 6.
// This peculiar encoding was chosen to reduce the source code size.
// Enum temporaries are used in order to ensure compile-time evaluation.
#define t(w8,w7,w6,w5,w4,w3,w2,w1,w0) if( \
        (o8<1?w0##u : o8<2?w1##u  : o8<3?w2##u : o8<4?w3##u : \
         o8<5?w4##u : o8<6?w5##u  : o8<7?w6##u : o8<8?w7##u : w8##u) & o8m)

t(0,0xAAAAAAAA,0x00000000,0x00000000,0x00000000,0xAAAAA2AA,0x00000000,0x00000000,0x00000000) { c = t; t += A + P.C; P.V = (c^t) & (A^t) & 0x80; P.C = t & 0x100; }

总之,我的一般问题:

  • 将CPU指令的惊人宇宙力量压缩成一小段代码

在15分钟来源(上面发布的部分)中特定于SNES模拟器的问题:

  • t(0, 0xAAAAAAAA, 0x00000000, ....)如何解析指令?我看到if声明,但我不知道任何参数的数字来自哪里,或者它们对整体代码的含义。
  • 为什么o8 = op / 32o8m = 1u << (op%32)
  • ADC的操作码ADC #const具有2字节操作数,或ADC addr具有3字节操作数。代码t(0, 0xAAAAAAAA, ...)是否适用于这两种情况?

我在问:

  • dp_dp_sr中显示的ADC dpADC (_dp_)ADC sr,S是什么意思?
  • ADC (_dp_,X)ADC dp,X之间有什么区别? (考虑到上面的问题,可能是冗余的。)

1 个答案:

答案 0 :(得分:4)

我无法回答所有这些问题,但dp代表Direct Page,这意味着该指令采用单字节操作数,该操作数是Direct Page中的内存地址。直接页面寻址是6502的零页面寻址模式的扩展,其中单字节地址指向存储器位置$00$FF。 6502的16位导数有一个配置寄存器,它基本上将零页重新定位到备用位置.f

在您链接到的Wiki页面中,表格中的某些dp会在其上显示下划线,其他的则以斜体显示。我假设它们都是斜体,并且wiki标记不起作用。快速检查Edit链接是否支持这一假设(在wiki源代码中,它们都有下划线)。所以不要读任何东西。

在6502汇编及其衍生词中,ADC dp,X表示......让我们举一个具体的例子...... ADC $10,X表示将$10添加到寄存器{{1}中的值获取一个地址,然后从该地址加载一个值并将其添加到累加器。 X添加了额外的间接级别:将ADC ($10,X)添加到$10以获取地址,从该地址加载值,将加载的值解释为另一个地址,并从< em> 地址并将其添加到累加器。带括号的操作数总是添加一个间接级别。

请注意,可用模式包括X(dp,X),并且括号相对于逗号和寄存器的位置非常重要。使用(dp),Y时,(dp),Y的值会添加到第一个加载的值中,以获取第二次加载时使用的地址。

至于那个模拟器......代码高尔夫不会提高可读性!我不认为你发布的那部分本身是可以理解的,而且我不想跟踪并阅读剩下的部分。但Y宏中的关键概念是 bitstring 。它的参数是一系列9位掩码,每个32位长,总共288位。因此,每个可能的操作码(其中256个)加上第一个注释中提到的3个伪操作码,在这个288位长的位串中由一个位表示,剩下29位。

这解释了to8的构建。 8位值被分成3位部分(从提供给o8m的8个参数中选择一个参数)和5位部分(从所选参数中选择一个位)。第t个大链会进行第一次选择,?:&的组合会进行选择。

然后,哦,看,我们也有一个名为1 << ...的变量。它与宏无关。给他们相同的名字只是残酷。

也许我可以弄清楚bittring正在做什么。当操作码数较小时,t(高位)将为0,因此o8链将使用?:,这是最后参数到宏。随着操作码的增加,所选参数向左移动到参数列表w0,然后w1 ... w2选择器同样从右侧开始向左移动({{1最右边的位,o8m是下一个,等等。)当所选位为1时,& (1<<0)条件为真。值为:

& (1<<1)

或二进制

if

从右到左阅读每一行,1位于与这些操作码对应的位置:0, # opcodes $100 and up 0xAAAAAAAA, # opcodes $E0 to $FF 0x00000000, # opcodes $C0 to $DF 0x00000000, # opcodes $A0 to $BF 0x00000000, # opcodes $80 to $9F 0xAAAAA2AA, # opcodes $60 to $7F 0x00000000, # opcodes $40 to $5F 0x00000000, # opcodes $20 to $3F 0x00000000 # opcodes $00 to $1F 0, # opcodes $100 and up 0b10101010101010101010101010101010, # opcodes $E0 to $FF 0b00000000000000000000000000000000, # opcodes $C0 to $DF 0b00000000000000000000000000000000, # opcodes $A0 to $BF 0b00000000000000000000000000000000, # opcodes $80 to $9F 0x10101010101010101010001010101010, # opcodes $60 to $7F 0b00000000000000000000000000000000, # opcodes $40 to $5F 0b00000000000000000000000000000000, # opcodes $20 to $3F 0b00000000000000000000000000000000 # opcodes $00 to $1F $61 $63 $65 {{1} } $67 $69 $6D $6F $71 $73 $75 $77 $79 {{1} } $7B $7D $7F $E1 $E3 $E5 $E7 $E9 $EB {{1} } $ED $EF $F1 $F3 $F5

嗯......那类似于$F7$F9操作码的列表,但其中一些是错误的。

哦(我终于放弃并查看了更多的仿真器代码),这是一个 NES 仿真器,而不是 SNES 仿真器,所以它只有6502个操作码。