在将旧的 Turbo Pascal 单位转换为现代的 Object Pascal 时,我遇到了以下问题:
function Less (var a, b; Relation : POINTER) : boolean;
inline($5B/$59/$0E/$E8/$00/$00/$58/$05/$08/$00/$50/$51/$53/$CB);
代码应该调用
external function {$F+} function VariableLess(var a, b : Index) : boolean; {$F-}
,收集结果并将其传递给调用函数。该函数用于为无类型数据提供二叉树的单元中
procedure InsVarBBTree(var B: BBTree; var E; S: word; A: pointer; var ok: boolean);
{ puts variable E of size S into tree B. The order relation address is A. }
因此,该单元本身无法提供比较功能,这是定义有效负载的单元的工作。
使用在线反汇编器我发现这对应于:
{$ASMMODE intel}
function Less (var a, b; Relation : POINTER) : boolean; assembler;
asm
pop bx
pop cx
push cs
call 6
pop ax
add ax, 8
push ax
push cx
push bx
retf
end;
但是,编译器不喜欢
push
语句。我应该怎样做才能让它在现代 64 位机器上运行?我意识到代码是 16 位的。
我刚刚在 Turbo Pascal 5 for MS-DOS 上编译了一些
inline
函数来检查 Turbo Pascal 如何生成代码:
对于非
inline
函数调用,Turbo Pascal 将所有函数参数推送到堆栈。第一个被先推入(因此 SS:SP
指向 last 函数参数)。然后执行 (far
) call
。函数使用 retf n
返回,这意味着调用的函数会从堆栈中删除所有参数。
在
inline
函数中,给出的原始字节只是 replace call
指令。这意味着 SS:SP
指向参数,而不是返回地址。内联机器语言代码必须pop
来自堆栈的参数。并且它不能使用 ret
返回,而只是在 inline
代码之后的指令处继续执行代码。
有了这些知识,就可以分析汇编代码了:
使用给出的汇编代码,您可以通过编写具有以下内容的辅助函数(在您的情况下:function
)间接调用带有任何参数(在您的情况下:
procedure
)的any
VariableLess
或Less
)与要调用的函数相同的参数加上指向实际函数的附加参数。
该代码等同于以下 Delphi 或 FreePascal 代码:
type
TMyCompare = function(var a, b) : boolean;
function Less (var a, b; Relation : TMyCompare) : boolean;
begin
Less := Relation(a, b);
end;
如果你的编译器支持函数指针(
type TMyCompare = function ...
),你可以这样做。
或者您甚至可以将程序中所有出现的
Less(x,y,z)
替换为 z(x,y)
。这样效率会更高。
当然,如果你这样做的话,指向函数(
VariableLess
)的指针不应该具有类型pointer
,而应该具有TMyCompare
类型。
如果您的编译器不支持函数指针(Turbo Pascal 显然不支持),您可能需要汇编。
但那样的话,不同的编译器将需要不同的汇编代码!
因此,不了解编译器的内部结构,就不可能翻译汇编代码。
编辑
我不确定你的编译器到底是如何工作的。但是,如果我的原始代码不起作用,也许下面的代码可以起作用:
function Less (var a, b; Relation : Pointer) : boolean;
type
TMyCompare = function(var a, b) : boolean;
var
Relation2 : TMyCompare;
begin
Relation2 := TMyCompare(Relation);
Less := Relation2(a, b);
end;
解决方法如下: 在处理动态类型的单元内部定义
type
TMyCompare = function(var a, b) : boolean;
function Less (var a, b; Relation : TMyCompare) : boolean;
begin
Result := Relation(a, b);
end;
procedure InsVarBBTree(var B: BBTree; var E; S: word; A: TMyCompare;
var ok: boolean);
{ puts variable E of size S into tree B. The order relation address is A. }
这是从外部调用的
{$F+} function VariableLess(var a, b : Index) : boolean; {$F-}
begin
...
end;
InsVarBBTree(Baum, TempStr, SizeOf(TempStr), TMyCompare(@VariableLess), OK)
感谢所有提供帮助的人
恩格尔伯特
Afaik 它适用于 Free Pascal 16 位目标,但您可能对此不感兴趣。 对于非16位目标,16位代码将需要重写,内存模型和ABI是不同的。 您的反汇编是象征性的,并且常量并不像原始汇编源(.asm)那样真正反映源的含义。
这个过程特别复杂,因为它似乎对 ABI 做出了假设,所以它本质上是不可移植的。
此外,即使你成功了,结果也不是最优的,因为对象 pascal 编译器(Delphi、Free Pascal)比 TP 优化得多。对短程序使用外部汇编器会阻碍编译器内联的能力。
我认为 Peter Cordes 是对的,这是在所示过程之后对实际功能的一种尝试。不,我认为 Martin 更接近。这有点不合逻辑,但因为编译器无法真正内联(仅转储汇编器块),参数调用保持不变,并且必须在不访问堆栈帧/局部变量的情况下撤消。 TP 不会将值保存在寄存器中,因此相对安全。
你也可以尝试反汇编它,但最好的可能是简单地尝试从文档中制定一个 pascal 替代品,而忘记所有的微观优化。
在 64 位上还有一些其他命令大小名称。 并且有必须调用的汇编程序, 也许不是为了这个“少”。 此外,变量大小名称是 rAX,...