如何在汇编语言X86中检测溢出条件

问题描述 投票:0回答:3

我有一个作业,我们必须编写两个函数。还必须使用处理器的条件代码检测溢出条件,并返回0表示已遇到错误。我能够编写函数。

 .file  "formula.c"  
    .text
.globl _nCr  
    .def    _nCr;   .scl    2;  .type   32; .endef  
_nCr:  
        pushl   %ebp  
    movl    %esp, %ebp  
    subl    $56, %esp  
    movl    8(%ebp), %eax  
    movl    %eax, (%esp)  
    testl %eax, %eax  
    call    _factorial  
    movl    %eax, -12(%ebp)  
    movl    12(%ebp), %eax  
    addl    $1, %eax  
    movl    %eax, (%esp)  
    call    _factorial  
    movl    %eax, -16(%ebp)  
    movl    12(%ebp), %eax  
    notl    %eax  
    addl    8(%ebp), %eax  
    movl    %eax, (%esp)  
    call    _factorial  
    movl    %eax, -20(%ebp)  
    movl    -16(%ebp), %eax  
    movl    %eax, %edx  
    imull   -20(%ebp), %edx  
    movl    %edx, -28(%ebp)  
    movl    -12(%ebp), %eax  
    movl    %eax, %edx  
    sarl    $31, %edx  
    idivl   -28(%ebp)  
    leave  
    ret  
.globl _factorial   
    .def    _factorial;  .scl    2;     .type   32;     .endef   
_factorial:  
    pushl   %ebp  
    movl    %esp, %ebp  
    subl    $16, %esp  
    movl    $1, -8(%ebp)  
    movl    $1, -4(%ebp)  
    jmp L3  
L4:   
    movl    -8(%ebp), %eax   
    imull   -4(%ebp), %eax  
    movl    %eax, -8(%ebp)  
    addl    $1, -4(%ebp)   
L3:
    movl    -4(%ebp), %eax  
    cmpl    8(%ebp), %eax  
    jle L4  
    movl    -8(%ebp), %eax  
    leave  
    ret  
    .def    ___main;    .scl    2;  .type   32; .endef  
    .section .rdata,"dr"  
    .align 4  

此功能基本上是nCr = n! / (r! (n-r)!)。当数字变大时,阶乘会发生溢出。

我只是不明白如何设置溢出条件。

c assembly x86 integer-overflow
3个回答
3
投票

1)您的算术命令是可能设置溢出位的操作

2)“ JO”(溢出时跳转)和“ JNO”(不溢出时跳转)允许分支,具体取决于是否发生溢出

3)您可能只是在“ JO”之后将“%eax”设置为0。

4)优秀的资源,如果您还不熟悉的话:

Programming from the Ground Up, Jonathan Bartlett


3
投票

在x86架构上,当执行诸如addl 8(%ebp), %eax之类的算术指令时,条件代码将设置在CPU状态字中。有些指令的行为取决于条件代码。

您可以让代码在给定条件下采用替代路径(执行分支)。 x86在Jxx助记符下有一系列条件分支指令:JA, JAE, JB, JBE, JC, JCXZ, ..., JZ。例如,JZ表示如果为零则跳转:如果指令产生的结果为零,则转移一个分支,并设置零标志。 JO在溢出时跳转。

条件也可以转换为字节数据并存储到寄存器或存储器中。这对于编译如下的C表达式很有用:

 x = (y != 3); /* if (y != 3) x = 1; else x = 0 */

[这是通过SETx组指令完成的,这些指令也很多,例如条件分支:SETA, SETAE, SETB, ..., SETZ。例如,如果零条件为真,则SETZ将给定字节设置为1。例如:

 seto %bl  /* set bottom byte of B register to 1 if overflow flag set */

0
投票

大多数指令在有符号溢出时设置为OF,在无符号溢出时设置为CF。 http://teaching.idallen.com/dat2343/10f/notes/040_overflow.txt说明加/减。 (像和/或/ xor之类的按位布尔指令不会溢出,因此它们总是清除CF和OF)。

当完整结果不是下半部分结果(与输入相同的宽度)的

符号扩展时,imul设置OF和CF。这甚至适用于高效的2操作数形式,该形式不会在任何地方写高半部分。他们仍然根据情况设置标志。如果对乘法进行无符号溢出检测,则需要使用笨拙的一操作数imul

当商不适合AL / AX / EAX / RAX(取决于操作数大小)时,除法会引发#DE异常。不幸的是,没有办法抑制/掩盖此现象,因此,除非有信号处理程序来捕获SIGFPE(在POSIX OS上),否则您不能尝试使用大分红的2N / N => N位除法并事后检测到溢出。 。或在裸机上,使用#DE的中断处理程序。

具体用于组合:

不是天真的计算mul,而是可以更早地取消,而只需计算n!。实际上使用较大的或prod(r+1 .. n)r,然后除以另一个。

您仍然要除以一个可能的大数,因此这不会消除适合32位整数的所有可能结果发生溢出的机会。但是它扩展了您可以轻松处理的范围,并且当然会更快,因为您执行的乘法次数较少。例如n-r只做C(999, 1000),所以不相乘,只有一个1000 / (1000-999)!

如果用div指令对乘积进行最后的乘法运算以在EDX:EAX中产生64位结果,则可以将其用作32位除法的64位除数。 (如果您想冒例外的危险。)

mul是NxN => 2N乘法,因此,如果仅在循环中使用它,它将忽略先前输出的上半部分。如果您按从低到高的顺序进行乘法运算,那么最后的乘法运算就是该范围的高端,这将为您提供最大的范围。

例如,您可能会这样做

mul

未经测试且未经过仔细考虑,可能是循环设置/范围内的一次性错误/特例错误

这是我正在描述的那种事情:我们可以在累加产品时检查溢出,除了最后一步我们允许产生64位结果。

[可能在某些情况下,prod循环的最后一个// mix of C pseudocode and AT&T 32-bit. // Some real registers, some var names: pick registers for those. if (n == r) return 1; divisor = factorial(min(r, n-r)); // and save in another register until later eax = max(r,n-r) + 1; // prod xor %edx, %edx # in case we skip the mul cmp n, %eax jae endprod # loop might need to run 0 times, but n==r case already handled lea 1(%eax), %ecx # i = low+1. Not overflow-checked jmp loop_entry prod_loop: # do{ imul %ecx, %eax # prod *= i for i=[max+2 .. n-1] jo overflow_in_prod # maybe need mul to avoid spurious signed but not unsigned overflow cases inc %ecx loop_entry: cmp n, %ecx jb prod_loop # }while(i<n) # leave the loop with ECX = n, with one multiply left to do mul %ecx # EDX:EAX = n * EAX # We're keeping the full 64-bit result, therefore this can't overflow endprod: div divisor # prod /= the smaller factorial # EDX:EAX / divisor, quotient in EAX. Or will raise #DE if it didn't fit. ret overflow_in_prod: do something ret 将产生一个32位无符号结果,并设置了高位,但没有无符号溢出。使用imul / jo会错误地将其检测为溢出,因为它是signed

溢出。最后的div不会溢出。因此,如果您不仅仅关心速度,还可以在其中使用(稍微慢一些)imul

无论如何,这使我们在prod(10 .. 18)= mul的情况下处理C(18, 9)。最后一个指令将产生EAX = 0x41b9e4200,它适合32位,而最后一个0x3a6c5900将其乘以18以产生EDX:EAX = mul(35个有效位)。然后,将其除以0x41b9e4200,得到EAX = 0xbdec。

9! = 0x58980n较大时,EDX:EAX中的有效位数会更大(但要紧靠在一起,所以我们仍然避免溢出)。它们必须相距足够远,以使r除数足够大,以使最终结果降低至适合32位。

否则,您将需要扩展精度除法,这可能...

© www.soinside.com 2019 - 2024. All rights reserved.