FreePascal 是正确的。 MUL只有3种形式:
MUL r/m8
MUL r/m16
MUL r/m32
执行第一个操作数(目标操作数)和第二个操作数(源操作数)的无符号乘法,并将结果存储在目标操作数中。 目标操作数是位于寄存器 AL、AX 或 EAX 中的隐含操作数(取决于操作数的大小);源操作数位于通用寄存器或内存位置。
换句话说,第一个操作数(用于输入和输出)在AL/AX/EAX 中指定,第二个输入操作数明确指定为通用寄存器或内存地址。
所以,MUL EAX,EDX 确实是一条无效的汇编指令。
如果您在 Delphi 中编译此代码并使用调试器查看生成的程序集,您会看到对 Mul(10,20) 的调用会生成以下程序集代码:
// Mul(10,20)
mov edx,$00000014
mov eax,$0000000a
call Mul
//MUL EAX,EDX
mul edx
因此,如您所见,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。来自德尔福的documentation:
x / y 的值是Extended 类型,与x 和y 的类型无关。对于其他算术运算符,只要至少有一个操作数是实数,结果就是Extended 类型;否则,当至少一个操作数为Int64 类型时,结果为Int64 类型; 否则,结果的类型为Integer。如果操作数的类型是整数类型的子范围,则将其视为整数类型。
它还使用一些难看的臃肿程序集,除非堆栈帧被禁用并启用优化。通过配置这两个选项,可以让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