实现CPU指令的仿真?
我正在努力学习仿真编程。我用40条指令做了一个芯片8模拟器,因为我的音乐而活了下来。我现在希望做一些更复杂的事情,比如SNES。我遇到的问题是CPU指令的数量太多了。从后面看,这就像是后部的疼痛。我在不同的网页上看到过这样或那样的注释,CPU是模拟器中最容易实现的部分 假设这很难,因为我做错了,我环顾四周,发现了一个简单的实现:大约900行代码。很容易完成 然后,从源代码中,我找到了CPU指令的位置。看起来比我想的简单多了。我真的不明白,但这只是几行代码,而不是大量的代码。我注意到的第一件事是每个指令只有一个实现。如果你看这张桌子,你会发现它有实现CPU指令的仿真?,c,emulation,C,Emulation,我正在努力学习仿真编程。我用40条指令做了一个芯片8模拟器,因为我的音乐而活了下来。我现在希望做一些更复杂的事情,比如SNES。我遇到的问题是CPU指令的数量太多了。从后面看,这就像是后部的疼痛。我在不同的网页上看到过这样或那样的注释,CPU是模拟器中最容易实现的部分 假设这很难,因为我做错了,我环顾四周,发现了一个简单的实现:大约900行代码。很容易完成 然后,从源代码中,我找到了CPU指令的位置。看起来比我想的简单多了。我真的不明白,但这只是几行代码,而不是大量的代码。我注意到的第一件事是每
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; }
//注意:op 0x100表示“NMI”,0x101表示“重置”,0x102表示“IRQ”。它们是根据“BRK”实现的。
//用户负责确保在处理重置时WB()不会存储到内存中。
无符号addr=0,d=0,t=0xFF,c=0,sb=0,pbits=op我不能回答所有这些问题,但是dp
代表直接页,这意味着指令采用单字节操作数,它是直接页中的内存地址。直接页寻址是6502零页寻址模式的扩展,其中单字节地址指的是内存位置$00
到$FF
。6502的16位衍生物有一个配置寄存器,基本上将零页重新定位到另一个位置。f
在您链接到的wiki页面中,表中的一些dp
上有下划线,其他的是斜体。我假设它们都是斜体的,并且wiki标记不起作用。对编辑链接的快速检查支持这种假设(在wiki源代码中,它们都有下划线)。所以不要读任何东西
在6502组件及其衍生产品中,ADC-dp,X
表示。。。让我们举出一个具体的例子来代替…code>ADC$10,X
表示将$10
添加到寄存器X
中的值以获得地址,然后从该地址加载一个值并将其添加到累加器中ADC($10,X)
添加了额外的间接寻址级别:将$10
添加到X
以获取地址,从该地址加载值,将加载值解释为另一个地址,然后从该地址加载值并将其添加到累加器。括号内的操作数总是添加一级间接寻址
请注意,可用模式包括(dp,X)
和(dp),Y
,括号相对于逗号和寄存器的位置非常重要。使用(dp),Y
将Y
的值添加到第一次加载的值中,以获得在第二次加载中使用的地址
至于那个模拟器。。。代码高尔夫不会提高可读性!我不认为你发布的那部分内容本身是可以理解的,我也不想追踪和阅读它的其余部分。但是t
宏中的关键概念是位字符串。它的参数是一系列9位掩码,每个32位长,总共288位。因此,每个可能的操作码(256个),加上第一条注释中提到的3个伪操作码,都由这个288位长的位字符串中的一个位表示,剩余29位
这就解释了o8
和o8m
的构造。8位值分为3位部分(从提供给t
的8个参数中选择一个参数)和5位部分(从所选参数中选择一个位)。大的?:
链执行第一个选择以及&
和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