Delphi asm to purepascal



我正在尝试将Delphi 5代码迁移到Delphi XE7-WIN64,并且在以下块中遇到了混合汇编代码的问题。我也是新手。

procedure IterateMenus(Func: Pointer; Menu1, Menu2: TElMenuItem);
var
  I, J: Integer;
  IIndex, JIndex: Byte;
  Menu1Size, Menu2Size: Integer;
  Done: Boolean;
  function Iterate(var I: Integer; MenuItem: TElMenuItem; AFunc: Pointer): Boolean;
  var
    Item: TMenuItem;
  begin
    if MenuItem = nil then Exit;
    Result := False;
    while not Result and (I < MenuItem.Count) do
    begin
      Item := MenuItem[I];
      if Item.GroupIndex > IIndex then Break;
      asm
        MOV     EAX,Item
        MOV     EDX,[EBP+8]
        PUSH    DWORD PTR [EDX]
        CALL    DWORD PTR AFunc
        ADD     ESP,4
        MOV     Result,AL
      end;
      Inc(I);
    end;
  end;
begin
  I := 0;
  J := 0;
  Menu1Size := 0;
  Menu2Size := 0;
  if Menu1 <> nil then
    Menu1Size := Menu1.Count;
  if Menu2 <> nil then
    Menu2Size := Menu2.Count;
  Done := False;
  while not Done and ((I < Menu1Size) or (J < Menu2Size)) do
  begin
    IIndex := High(Byte);
    JIndex := High(Byte);
    if (I < Menu1Size) then
      IIndex := Menu1[I].GroupIndex;
    if (J < Menu2Size) then
      JIndex := Menu2[J].GroupIndex;
    if IIndex <= JIndex then
      Done := Iterate(I, Menu1, Func)
    else
    begin
      IIndex := JIndex;
      Done := Iterate(J, Menu2, Func);
    end;
    while (I < Menu1Size) and (Menu1[I].GroupIndex <= IIndex) do
      Inc(I);
    while (J < Menu2Size) and (Menu2[J].GroupIndex <= IIndex) do
      Inc(J);
  end;
end;

我正在尝试将 asm 块转换为 purepascal,因为 delphi x64 不允许混合代码,并且 asm 对程序员不友好。

据我了解,项目的地址已移至EAX,然后我什么也得不到。EBP从何而来?什么是 ESP 和 AL?上面的代码片段来自ELPack的ELmenus.pas。

那么基本上什么是 asm 代码块的 PurePascal 版本?

对于函数,我发现这个

procedure TElMenuItem.UpdateItems;
  function UpdateItem(MenuItem: TElMenuItem): Boolean;
  begin
    Result := False;
    IterateMenus(@UpdateItem, MenuItem.FMerged, MenuItem);
    MenuItem.SubItemChanged(MenuItem, MenuItem, True);
  end;
begin
  IterateMenus(@UpdateItem, FMerged, Self);
end;
procedure TElMenuItem.PopulateMenu;
var
  MenuRightToLeft: Boolean;
  function AddIn(MenuItem: TElMenuItem): Boolean;
  begin
    MenuItem.AppendTo(FHandle, MenuRightToLeft);
    Result := False;
  end;
begin    // all menu items use BiDiMode of their root menu
  {$ifdef VCL_4_USED}
  MenuRightToLeft := (FMenu <> nil) and FMenu.IsRightToLeft;
  {$else}
  MenuRightToLeft := false;
  {$endif}
  IterateMenus(@AddIn, FMerged, Self);
end;

我猜原始程序员编写了奇怪的代码来绕过不支持本地函数指针的Delphi。(这是一个有时让我恼火的限制。

以下应该有效。(虽然我不确定我的 asm 那么敏锐。我将首先通过单步执行 D5 中的调试器来测试 asm 代码,以确认行CALL DWORD PTR AFunc传递了我预期的值。

1) Delcare 一个函数指针类型并移动 UpdateItem 使其不再是本地的。

type
  TMenuOperation = function (AMenuItem: TElMenuItem): Boolean;
  //Or if you want to move the UpdateItem to a method of a class..
  //TMenuOperation = function (AMenuItem: TElMenuItem): Boolean of object;

2) 进行以下更改:

procedure IterateMenus(Func: Pointer; Menu1, Menu2: TElMenuItem);
//becomes
procedure IterateMenus(Func: TMenuOperation; Menu1, Menu2: TElMenuItem);
//...
  function Iterate(var I: Integer; MenuItem: TElMenuItem; AFunc: Pointer): Boolean;
  var
    Item: TMenuItem;
  //becomes
  function Iterate(var I: Integer; MenuItem: TElMenuItem; AFunc: TMenuOperation): Boolean;
  var
    Item: TElMenuItem;
  //...
      Item := MenuItem[I];
      //becomes
      Item := MenuItem[I] as TElMenuItem;

3)最后汇编块变成:

Result := AFunc(Item);

该 asm 代码正在调用 Item 上的方法。我想说的是,无论谁编写代码,都需要检查他们的头脑。就好像他们不知道方法指针一样。

我解决这个问题的方法是查找调用函数的代码,并查看作为Func参数传递的内容。这就是asm块所称呼的。将 Func 参数的类型更改为适当的过程类型,并将 asm 块替换为对该方法指针的调用。


好的,现在我看到原始代码在本地过程中快速而松散地播放。您应该做的是Func成为匿名方法类型:

Func: TFunc<TElMenuItem, Boolean>;

然后将本地函数转换为匿名方法。


升级

德尔福版本的常用方法是同时升级第三方库。如果你这样做,那么我想你不需要移植 15 年前的图书馆代码。

最新更新