Delphi FPC BASM32 MUL错误?

Delphi FPC BASM32 MUL错误?,delphi,lazarus,freepascal,fpc,basm,Delphi,Lazarus,Freepascal,Fpc,Basm,我在将Delphi BASM32代码移植到FPC时遇到一个问题: program MulTest; {$IFDEF FPC} {$mode delphi} {$asmmode intel} {$ELSE} {$APPTYPE CONSOLE} {$ENDIF} function Mul(A, B: LongWord): LongWord; asm MUL EAX,EDX end; begin Writeln(Mul(10,20)); Readln

我在将Delphi BASM32代码移植到FPC时遇到一个问题:

program MulTest;

{$IFDEF FPC}
  {$mode delphi}
  {$asmmode intel}
{$ELSE}
  {$APPTYPE CONSOLE}
{$ENDIF}

function Mul(A, B: LongWord): LongWord;
asm
         MUL    EAX,EDX
end;

begin
  Writeln(Mul(10,20));
  Readln;
end.
上述代码在Delphi XE中编译并按预期工作;FPC在
MUL EAX,EDX
行上输出编译时错误:

错误:Asm:[mul reg32,reg32]操作码和密码的组合无效 操作数

我正在使用Lazarus 1.4.4/FPC2.6.4 for Win32(当前稳定版本)


该问题的任何解决方法或修复方法?

MUL总是乘以AL、AX或EAX(),因此您应该只指定另一个操作数。

FreePascal是正确的。只有以下三种形式:

对第一个操作数(目标操作数)和第二个操作数(源操作数)执行无符号乘法,并将结果存储在目标操作数中目标操作数是位于寄存器AL、AX或EAX中的隐含操作数(取决于操作数的大小);源操作数位于通用寄存器或内存位置

换句话说,第一个操作数(用于输入和输出)在
AL
/
AX
/
EAX
中指定,第二个输入操作数明确指定为通用寄存器或内存地址

因此,
MUL-EAX,EDX
确实是一条无效的汇编指令

如果您在Delphi中编译此代码并使用调试器查看生成的程序集,您将看到对
Mul(10,20)
的调用生成以下程序集代码:

// Mul(10,20)
mov edx,$00000014
mov eax,$0000000a
call Mul

因此,正如您所看到的,Delphi正在实际解析您的源代码,看到第一个操作数是
EAX
,并为您将其剥离,从而生成正确的程序集。FreePascal并不是为你做这一步

解决办法?首先编写正确的汇编代码。不要依赖编译器为您重新解释代码

function Mul(A, B: LongWord): LongWord;
asm
         MUL    EDX
end;
或者,您不能直接编写汇编代码,让编译器为您完成这项工作。它知道如何将两个
LongWord
值组合在一起:

function Mul(A, B: LongWord): LongWord;
begin
  Result := A * B;
end;
尽管在这种情况下,Delphi使用了
IMUL
而不是
MUL
。德尔福:

无论
x
y
的类型如何,
x/y
的值都属于
扩展的
类型。对于其他算术运算符,只要至少有一个操作数是实数,则结果类型为
Extended
;否则,当至少一个操作数的类型为
Int64
时,结果的类型为
Int64
否则,结果的类型为
Integer
。如果操作数的类型是整数类型的子范围,则将其视为整数类型

除非禁用stackframes并启用优化,否则它还使用一些难看的臃肿部件。通过配置这两个选项,可以获得
Mul()
以生成单个
IMUL EDX
指令(当然还有
RET
指令)。如果不想在项目范围内更改选项,可以使用
{$STACKFRAMES OFF}
/
{$W-}
{$OPTIMIZATION ON}
/
{$O+}
编译器指令将它们隔离为
Mul()

{$IFOPT W+}{$W-}{$DEFINE SF_Was_On}{$ENDIF}
{$IFOPT O-}{$O+}{$DEFINE O_Was_Off}{$ENDIF}
function Mul(A, B: LongWord): LongWord;
begin
  Result := A * B;
end;
{$IFDEF SF_Was_On}{W+}{$UNDEF SF_Was_On}{$ENDIF}
{$IFDEF O_Was_Off}{O-}{$UNDEF O_Was_Off}{$ENDIF}
生成:

imul edx
ret

Remy,这只是一个独立的问题:我可以看到您正在开始/结束对中设置编译器指令,并且之后不会重置它们。这是因为通过在函数体中设置它们,它们仅是该函数的本地对象吗?每当我需要临时设置一些编译器指令时,我总是沿着{$IFOPT W+}{$DEFINE WASON}{$W-}{$ENDIF}和{$IFDEF WASON}{$W+}{$ENDIF}做一些事情,但是如果可以通过将“本地”编译器指令放在主体中来实现,那么这是不必要的。我提到的两个指令都有局部作用域,但我认为我对与编译器指令相关的局部范围的理解是错误的。我已经更新了我的示例。在FPC中,您可以使用$push/$pop恢复状态。@MarcovandeVoort:True。Delphi没有与之等价的产品(我希望有)。
{$IFOPT W+}{$W-}{$DEFINE SF_Was_On}{$ENDIF}
{$IFOPT O-}{$O+}{$DEFINE O_Was_Off}{$ENDIF}
function Mul(A, B: LongWord): LongWord;
begin
  Result := A * B;
end;
{$IFDEF SF_Was_On}{W+}{$UNDEF SF_Was_On}{$ENDIF}
{$IFDEF O_Was_Off}{O-}{$UNDEF O_Was_Off}{$ENDIF}
imul edx
ret