我正在创建一个X86解码器,我正在努力理解并找到一种有效的方法来计算指令的助记符。
我知道OpCode 6 MSB是OpCode位,但是我找不到在助记符表中使用这6位的任何地方。我发现的唯一助攻表是整个OpCode Byte本身,而不仅仅是6个MSB。
我想问什么有效的方法可以解码OpCode字节中编码的mnemonics,如果使用6个MSB而不是整个OpCode Byte。
但是没有有效的方法来存储无重复的助记符的表格吗?
这已成为一种算法和数据结构问题。
您指出的是,许多opcode表条目(至少对于没有0f
逃脱字节的表格:http://sparksandflames.com/files/x86instructionchart.html)确实在4或2的组中重复。使用相同的6或7位前缀选择相同的助记符。
显然,一张256个结构表很简单,但重复了事物。它非常快速且易于使用,因为它可能仍然足够小,无法经常缓存失误。(尤其是因为常见条目将在缓存中保持热; x86代码经常使用相同的操作编码。)
您可以为空间交易简单/性能。
您可以拥有一个64个结构表,其中一个成员是指向辅助表的指针,该表被低2位索引。如果指针为null,则表示指令遵循add
/and
/xor
/等的模式,其中低2位告诉您8位与操作数大小和方向(R/M,Reg或reg,Reg,Reg,r/m)。
当存在某些前缀时,您的结构还需要条目将其变成其他说明(例如rep nop
是pause
)。另外,AVX VEX前缀使用了过去的另一项指令的无效编码。如果您想为所有当前的扩展工作完成一份完整的工作,则X86非常疯狂。
实际上,仅使用功能pointers 的表格可能是最简单的(也是有效的)。或具有const char* mnemonic
和int (*decode)(const char*mnemonic, const char *insn_bytes, unsigned prefix_bitmap)
功能的结构,因此许多Opcodes都可以指向相同的解码功能,但仍然获得不同的mnemonics。有时,该功能会忽略传递的助记符,而其他时间就是它所需的。您将有一个通用函数用于解码许多解码功能会调用的地址模式。
这与您如何实现解释的X86模拟器相当相似,而不是进行动态重新编译。一个常见的解码循环,然后通过功能指针进行调度。
您可能使用的更复杂的数据结构是Radix Trie AKA前缀树。另请参见https://en.wikipedia.org/wiki/trie#bitwise_tries。
这已经进入了愚蠢的季节,因为密度太高了,以至于查找桌更有意义。(很少有未定义的OpCode)。