将 Turbo Pascal 内联代码转换为 Object Pascal

问题描述 投票:0回答:4

在将旧的 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 位的。

assembly inline pascal x86-16 turbo-pascal
4个回答
8
投票

我刚刚在 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
)与要调用的函数相同的参数加上指向实际函数的附加参数。

该代码等同于以下 DelphiFreePascal 代码:

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;

2
投票

解决方法如下: 在处理动态类型的单元内部定义

 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)

感谢所有提供帮助的人

恩格尔伯特


1
投票

Afaik 它适用于 Free Pascal 16 位目标,但您可能对此不感兴趣。 对于非16位目标,16位代码将需要重写,内存模型和ABI是不同的。 您的反汇编是象征性的,并且常量并不像原始汇编源(.asm)那样真正反映源的含义。

这个过程特别复杂,因为它似乎对 ABI 做出了假设,所以它本质上是不可移植的。

此外,即使你成功了,结果也不是最优的,因为对象 pascal 编译器(Delphi、Free Pascal)比 TP 优化得多。对短程序使用外部汇编器会阻碍编译器内联的能力。

我认为 Peter Cordes 是对的,这是在所示过程之后对实际功能的一种尝试。不,我认为 Martin 更接近。这有点不合逻辑,但因为编译器无法真正内联(仅转储汇编器块),参数调用保持不变,并且必须在不访问堆栈帧/局部变量的情况下撤消。 TP 不会将值保存在寄存器中,因此相对安全。

你也可以尝试反汇编它,但最好的可能是简单地尝试从文档中制定一个 pascal 替代品,而忘记所有的微观优化。


0
投票

在 64 位上还有一些其他命令大小名称。 并且有必须调用的汇编程序, 也许不是为了这个“少”。 此外,变量大小名称是 rAX,...

© www.soinside.com 2019 - 2024. All rights reserved.