分成大会

Shm*_*aev 3 c assembly x86-64 nasm division

我试图用Linux命令定义一个C语言的计算器,dc程序的结构并不那么重要,你需要知道我得到两个数字,我想在打字时划分它们/.因此,我将这两个数字发送到进行除法的汇编函数(参见下面的代码).但这仅适用于正数.

当输入999 3 /返回333是正确的,但在打字的时候-999 3 /,我得到了陌生号码1431655432,并输入两个负数时,像-999 -3 /任何两个负数每次我得到0.

程序集中的代码是:

section .text 
global _div 

_div: 
  push rbp            ; Save caller state 
  mov rbp, rsp 

  mov rax, rdi        ; Copy function args to registers: leftmost... 
  mov rbx, rsi        ; Next argument... 

  cqo
  idiv rbx            ; divide 2 arguments 
  mov [rbp-8], rax 

  pop rbp             ; Restore caller state 
Run Code Online (Sandbox Code Playgroud)

Mic*_*tch 7

你的评论说你要传递整数_idiv.如果您使用int的是32位值:

extern int _div (int a, int b);
Run Code Online (Sandbox Code Playgroud)

当传递给该函数a将在底部32位RDIb将在底部32位RSI.参数的高32位可以是垃圾,但通常是零,但不一定是这种情况.

如果使用64位寄存器与除数IDIV然后分工RDX:RAX/64位除数(在你的情况RBX).这里的问题是您使用完整的64位寄存器进行32位除法.如果我们假设RDIRSI的高位最初为0,那么RSI将为0x00000000FFFFFC19(RAX),RDI将为0x0000000000000003(RBX).CQORAX零扩展到RDX.RAX的最高位为零,因此RDX为零.该部门看起来像:

0x000000000000000000000000FFFFFC19/0x0000000000000003 = 0x55555408

0x55555408碰巧是1431655432(十进制),这是你看到的结果.一个解决方法是使用32位寄存器进行除法.要将EAX(低32位RAX)扩展到EDX,您可以使用CDQ而不是CQO.然后您可以通过EBX划分EDX:EAX.这应该是你正在寻找的32位签名部门.代码看起来像:

cdq
idiv ebx                 ; divide 2 arguments EDX:EAX by EBX
Run Code Online (Sandbox Code Playgroud)

请注意,RBX,RBP,R12R15都需要通过修改它们的功能来保留它们(它们是AMD 64位ABI中的易失性寄存器).如果您修改RBX,您需要确保像使用RBP一样保存和恢复它.更好的选择是使用一个易失性寄存器,如RCX而不是RBX.


您不需要中间寄存器来将除数放入.您可以直接使用RSI(或固定版本的ESI),而不是将其移动到RBX之类的寄存器.


眠りネ*_*ネロク 5

您的问题与传递参数的方式有关_div.

假设你_div的原型是:

int64_t _div(int32_t, int32_t);
Run Code Online (Sandbox Code Playgroud)

然后,参数被传入ediesi(即,32位有符号整数),则寄存器的上半部分rdirsi是未定义的.

符号扩展分配时,需要ediesiraxrbx用于执行64位的签名分裂(用于执行64位的无符号除法零扩展将被代替需要).

也就是说,而不是:

mov   rax, rdi       
mov   rbx, rsi 
Run Code Online (Sandbox Code Playgroud)

使用指令movsx,该符号扩展源,on ediesi:

movsx rax, edi
movsx rbx, esi
Run Code Online (Sandbox Code Playgroud)

使用真正的64位操作数进行64位除法

前一种方法consits上的"假"的64位操作数(即,执行64位分割的符号扩展的32位操作数).混合"32位操作数" 64位指令通常不是一个很好的主意,因为这可能会导致糟糕的性能和更大的代码大小.

更好的方法是简单地更改_div函数的C原型以接受实际的64位参数,即:

int64_t _div(int64_t, int64_t);
Run Code Online (Sandbox Code Playgroud)

这样,参数将传递给rdirsi(即已经是64位有符号整数),并且将对真正的64位整数执行64位除法.

改为使用32位除法

您可能还需要考虑使用32位的idiv,如果它适合你的需要,因为它的性能比64位除法更快,产生的代码尺寸更小(无REX前缀):

...   
mov eax, edi      
mov ebx, esi      
cdq
idiv ebx
...
Run Code Online (Sandbox Code Playgroud)

_div原型将是:

int32_t _div(int32_t, int32_t);
Run Code Online (Sandbox Code Playgroud)