繁体   English   中英

ASM /德尔福 - 分裂

[英]ASM/Delphi - Divide

我试图划分两个数字50和5.这是我的代码:

function Divide(Num1, Num2: Integer): Integer;
asm
  MOV   EAX, Num1
  CDQ
  MOV   ECX, Num2
  IDIV  ECX
  MOV   @RESULT, ECX
end;

它在Delphi中给了我一个DivisionByZeroException异常。 有人能告诉我我做错了什么吗?

这是CDQ指令。 来自在线参考

通过在EDX中扩展EAX的高位,将EAX中的带符号DWORD转换为EDX:EAX中的带符号四字

问题是, Num2是第二个参数,存储在EDX中,并且由于您在将EDX加载到ECX之前运行CDQ ,因此在ECX中结束的是0.重写它,并且您的例程按预期工作:

function Divide(Num1, Num2: integer): integer;
asm
  MOV EAX, Num1
  MOV ECX, Num2
  CDQ
  IDIV ECX
  MOV @Result, EAX
end;

梅森的答案是准确的,并清楚地解释了由于CDQ符号扩展覆盖EDX中的输入参数而导致的错误。 我不需要多说,梅森明白了。 并注意IDIV在EAX而不是ECX中返回商的修正。

我想尝试提供一些关于写作asm的更一般的建议。 我相信你的根本问题是在你的asm中使用参数名,而不是寄存器名。

由于您使用寄存器调用约定,因此明确指出参数到达寄存器这一事实确实是值得的。 如果你这样做,可能会更清楚发生了什么。 尝试使用变量名称会给你一种抽象的幻觉。 实际上,抽象不存在。 通过隐藏从视图传递的寄存器参数,您很难发现这样的错误,并且确定您踩到了输入!

首先让我们在寄存器方面用Mason的答案编写代码。 添加评论以增加清晰度。 像这样:

function Divide(Num1, Num2: integer): integer;
// Input: EAX: Num1, EDX: Num2
// Output: EAX: Result
asm
  MOV EAX, EAX
  MOV ECX, EDX
  CDQ
  IDIV ECX
  MOV EAX, EAX
end;

我们马上获得了直接好处,即第一行和最后一行显然毫无意义。 由于使用了变量名,您无法在您的版本中看到它。

所以我们可以像这样写:

function Divide(Num1, Num2: integer): integer;
// Input: EAX: Num1, EDX: Num2
// Output: EAX: Result
asm
  MOV ECX, EDX
  CDQ
  IDIV ECX
end;

当然,大多数算术运算在EAX中返回结果并且相同的寄存器用于函数返回值并非巧合。


关键是写asm就是了解寄存器的使用和重用。 不要用变量名来掩盖它。 保持注册使用的正面和中心,在视线中。 一旦你开始这样做,你就不会很难发现问题中的错误,并且当价值恰好落在正确的寄存器中时你将能够删除虚假操作。

我的建议是永远不要使用参数名称或Result asm代码。


另一个非常明显的一点是,您正在重新实现div运算符。 通过将其置于asm函数中,您不可避免地使代码效率降低,可读性降低。

对于它的价值,这个特定的功能实际上可以像Pascal一样更有效地编写。 考虑以下程序:

{$APPTYPE CONSOLE}

function DivideAsm(Num1, Num2: integer): integer;
// Input: EAX: Num1, EDX: Num2
// Output: EAX: Result
asm
  MOV ECX, EDX
  CDQ
  IDIV ECX
end;

function DividePas(Num1, Num2: integer): integer;
begin
  Result := Num1 div Num2;
end;

function DividePasInline(Num1, Num2: integer): integer; inline;
begin
  Result := Num1 div Num2;
end;

var
  i, j, k, l: Integer;

begin
  i := 666;
  j := 42;
  l := 0;
  inc(l, i div j);
  inc(l, DivideAsm(i, j));
  inc(l, DividePas(i, j));
  inc(l, DividePasInline(i, j));
  Writeln(l);
end.

现在, DividePasDivideAsm差。 前者通过优化编译为:

0040524C 53               push ebx
0040524D 8BDA             mov ebx,edx
0040524F 8BC8             mov ecx,eax
00405251 8BC1             mov eax,ecx
00405253 99               cdq 
00405254 F7FB             idiv ebx
00405256 5B               pop ebx
00405257 C3               ret

很明显DivideAsm因跳过prolog / DivideAsm获胜。

但是让我们看一下代码的主体:

SO22570866.dpr.28: i := 666;
004060D7 BE9A020000       mov esi,$0000029a
SO22570866.dpr.29: j := 42;
004060DC BF2A000000       mov edi,$0000002a
SO22570866.dpr.30: l := 0;
004060E1 33DB             xor ebx,ebx
SO22570866.dpr.31: inc(l, i div j);
004060E3 8BC6             mov eax,esi
004060E5 99               cdq 
004060E6 F7FF             idiv edi
004060E8 03D8             add ebx,eax
SO22570866.dpr.32: inc(l, DivideAsm(i, j));
004060EA 8BD7             mov edx,edi
004060EC 8BC6             mov eax,esi
004060EE E851F1FFFF       call DivideAsm
004060F3 03D8             add ebx,eax
SO22570866.dpr.33: inc(l, DividePas(i, j));
004060F5 8BD7             mov edx,edi
004060F7 8BC6             mov eax,esi
004060F9 E84EF1FFFF       call DividePas
004060FE 03D8             add ebx,eax
SO22570866.dpr.34: inc(l, DividePasInline(i, j));
00406100 8BC6             mov eax,esi
00406102 99               cdq 
00406103 F7FF             idiv edi
00406105 03D8             add ebx,eax

您可以看到编译器可以更自由地使用内联版本进行寄存器使用。 编译器不依赖于调用约定ABI。 这允许它发出更少的MOV操作。 实际上,内联引擎和优化器之间的交互非常好。 这是我写的代码的第一个版本:

inc(l, DivideAsm(i, j));
inc(l, DividePas(i, j));
inc(l, i div j);
inc(l, DividePasInline(i, j));

但优化者在最后两个声明中击败了我:

SO22570866.dpr.33: inc(l, i div j);
004060F9 8BC6             mov eax,esi
004060FB 99               cdq 
004060FC F7FF             idiv edi
004060FE 8BC8             mov ecx,eax
00406100 03D9             add ebx,ecx
SO22570866.dpr.34: inc(l, DividePasInline(i, j));
00406102 03D9             add ebx,ecx

优化器能够识别出ECX寄存器已经包含DividePasInline的结果并完全跳过代码!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM