我在将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(当前稳定版)
任何解决方法或修复问题?
FreePascal是正确的.只有3种形式MUL
:
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
在这种情况下.来自Delphi的文档:
的值
x / y
是类型的Extended
,无论类型中的x
和y
.对于其他算术运算符,Extended
只要至少有一个操作数是实数,结果就是类型; 否则,Int64
当至少一个操作数是类型时,结果是类型Int64
; 否则,结果是类型Integer
.如果操作数的类型是整数类型的子范围,则将其视为整数类型.
除非禁用堆栈帧并启用优化,否则它还会使用一些难看的膨胀组件.通过配置这两个选项,可以Mul()
生成单个IMUL EDX
指令(RET
当然还有指令).如果您不想在项目范围内更改选项,则可以Mul()
使用{$STACKFRAMES OFF}
/ {$W-}
和{$OPTIMIZATION ON}
/ {$O+}
编译器指令将它们隔离.
{$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
MUL总是乘以AL,AX或EAX(更多细节),因此您应该只指定另一个操作数.