拜读了egogg的大作《打造自己的反汇编引擎Intel指令编码学习报告》。对于opcode里的s位用debug32作了如下的实验:
   (1)初始状态 eax=00,00,00,00
          输入机器码:66,81,C0,F8  (debug32默认是16位模式,所以要用66前缀)
      对应的汇编: add eax,f8
         opcode = 81 = 100000[0]1   (opcode里的s位等于0,按0扩展)
         执行后   eax=00,00,00,F8
     (2)初始状态 eax = 00,00,00,00
         输入机器码:66,83,C0,F8
         对应的汇编:add eax,f8
        opcode = 83 = 100000[1]1 (opcode里的s位等于1)
        执行后   eax = FF,FF,FF,F8  (opcode里的s位等于1,按符号扩展)
     一切似乎都很顺利的验证了s位的功能。但当我想用nasm来做实验的时候,问题出现了,nasm为add所生成的机器码好像根本都不采用带符号扩展的add指令,而生成的机器码都是不带符号扩展的add指令。后来我才想明白,对于cpu来说,opcode=83的add指令是你传入一个单字节的数据让cpu自动为你扩展到32位,而对nasm来说完全没有必要让cpu去自动扩展,它的扩展是在编译期就实现了的。所以对汇编编译器来说,完全可以无视opcode=83的指令,甚至是所有s位等于1的指令格式都可以无视?值得注意的是opcode=81的add指令后面跟的是32位的立即数,opcode=83的add指令后面跟的是8位立即数。
     可能有人会问,那如果我传入add指令的是单字节的变量怎么办?答案很简单,汇编器里大部分都不允许源操作数与目的操作数的大小不同(除了一些诸如符号扩展指令),也就是说,其实汇编器不支持的源操作数与目的操作数大小不同的很多指令其实cpu是支持的,例如add,cpu其实是支持add eax,8位的数值的,只要在这条指令的opcode的s位置1。但就目前的大部分汇编器的情况来说,都限制了支持这种格式的指令或自动为你进行扩展。如果你用的是8位的常量(-128~127),那么汇编器会自动帮你按符号或按0扩展成32位,如果你用的是8位的变量,又要用到这些指令,您要在代码里通过符号扩展指令自己实现扩展后才能加入运算,否则会报错。
     例如 add eax,byte[变量]   ;汇编器会报错
          add eax,8                ;不报错,只是汇编器会自动帮你把8位的常量扩展到32位,但其实对于这条指令而言cpu并不要求这么做。
     不知我理解的对不对?这个对s的理解对一般写汇编的人来说可能意义不大,但对想理解汇编器生成的机器码或想写汇编器的人来说应该有帮助。
     之所以我会对这个问题去做实验,是因为当我在看到egogg那篇文章里的s位的时候,我的第一个反应是,当我往nasm里输入add eax,-8,或add eax,8的时候,汇编器凭什么来决定到底是生成81或83的机器码。
欢迎一起讨论。