在IA32 arch中,指令中使用的操作数是签名还是未签名?

时间:2013-12-29 16:01:36

标签: assembly x86 att

我想知道在使用IA32架构时,默认情况下是操作数已签名或未签名。

在Linux操作系统上,我使用as(GNU汇编程序)在%eax寄存器中存储253。存储在%al中的实际值(即eax的lsb)显示-3。我在这里假设al使用范围-127到+127而不是0到255.

我正在使用gdb来检查寄存器值。

因此想知道操作数是否默认在IA32体系结构中签名。

3 个答案:

答案 0 :(得分:2)

在汇编程序中,只有在做出决策(即条件跳转)时才能识别有符号值和无符号值。 ALU对有符号整数和无符号整数的操作方式相同。如果操作数和结果被解释为带符号数,则某些ALU标志(如OF(溢出)有意义。其他如CF(进位)如果被解释为无符号数则有意义。最后,像ZF(零)这样的其他人可以使用这两种解释。

例如:假设你使用的是8位寄存器(只是为了简单起见),你想出了这段代码:

mov $0xff,%al
mov $0x01,%bl
add %bl,%al

这个序列可以用两种方式解释(记住两个操作数和结果都是8位宽):

  • 添加-1 + 1,结果为0
  • 添加255 + 1,结果为0

您如何解释这取决于您对此操作的结果所做出的决定。想象一下,你想检查这个操作是否正常,结果没有任何溢出。

如果您认为您的值是无符号的,那么您添加了2551,从而产生了一个无法以8位存储的数字(256),因此存在超限并且实际结果不符合您的预期(0)。你必须查看进位标志来检查:

jc .operation_overrun

这个特殊的例子将引起这种跳跃,因为存在超限条件。

但如果您认为您的值已签名,则您添加了-11,等于0。要在此处检查溢出,您必须查看溢出标志:

jo .operation_overrun

这个特殊的例子会使这个跳转到而不是,因为没有超支。


另一个例子:假设你比较两个32位寄存器%eax%ebx中的两个数字。如果%eax中的数字大于%ebx中的数字,您想要跳转到其他地方。你怎么做到这一点?您必须决定如何解释您的数字,因为如果您可以提出,例如,EAX=0xFFFFFFFFEBX=0x00000001 ......

;...If both numbers are considered to be unsigned...
cmp %ebx,%eax  ;compare EAX with EBX
ja .another_place   ; ...this will jump because EAX=4294967295, 
                    ; which way much greater than EBX=1

;...But if both numbers are considered to be signed...
cmp %ebx,%eax  ;compare EAX with EBX
jg .another_place   ; ...this will NOT jump because EAX=-1, 
                    ; which is not greater than EBX=1

在这两种情况下,操作(比较,实际上是减法)再次相同,但我们做出的决定(我们在执行条件跳转时看到的标志)对于每种情况都是不同的。第一个跳转ja查看了进位标志(CF)和零标志(ZF)的值。第二次跳转jg查看了sign flag(SF)和溢出标志(OF)的值。

有关每个特定条件跳转使用哪些标志的详细列表,请参阅http://www.unixwiz.net/techtips/x86-jumps.html

答案 1 :(得分:1)

这只是一个解释问题。 8位值253和-3的位模式是相同的。执行操作时同样是解释问题,结果比特模式比如1 + 253(254)和1 - 3(-2)同样相同。

无论是为al(或许你的调试器)显示-3,只是因为缺少任何其他信息时的默认解释是签名。如果这是你想要的,你必须告诉它解释为无符号。

答案 2 :(得分:0)

寄存器包含位,就是这样。它的指示按摩和解释决定意义的那些位。不要被编译器,打印或其他调试例程产生的输出所困惑。

假设您使用al来存储二进制补码算法中的16位有符号整数,并且您希望将其转换为32位有符号整数。您必须对16位值执行符号扩展,以便将其转换为32位有符号整数。

例如,8位值1111 1110在二进制补码中为-2。 16值0000 0000 1111 1110不是-2,而是254.如果我们在从8位切换到16位时执行了符号扩展,那么我们就会从1111 1110(8位)开始版本-2)到1111 1111 1111 1110(16位版本的-2)。

还记得我是怎么说这些只是寄存器中的位而且它取决于你使用的指令给它们意义吗?在上面的例子中,你将使用指令cbw从二进制补码8位转换为二进制补码16位。来自维基百科,如何转换:

  

使用cbw,cwd,cwde和cdq指令:将字节转换为字,   双字的字,双字的字和双字的字   四字,分别(在x86上下文中,一个字节有8位,一个字   16位,双字和扩展双字32位,以及四字   64位);