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 не подходит для программистов.

Насколько я понимаю, адрес Item перенесен в EAX, тогда я ничего не получаю. Откуда взялся EBP? Что такое ESP и AL? Приведенный выше фрагмент кода взят из ELmenus.pas из ELPack.

Так в основном, какой будет версия PurePascal кодового блока asm?

Для func я нашел это

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;

2 ответа

Я предполагаю, что оригинальный программист написал странный код для обхода Delphi, не поддерживающего локальные указатели функций. (Что является ограничением, которое раздражало меня время от времени.)

Следующее должно работать. (Хотя я не настолько острый в своем asm, чтобы быть уверенным. Сначала я бы протестировал код asm, пройдя через отладчик в D5, чтобы убедиться, что строка CALL DWORD PTR AFunc передает значения, которые я ожидаю.)

1) Удалите указатель типа функции и переместите элемент 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>;

Затем преобразуйте локальные функции в анонимные методы.


Обычный подход к обновлению версий Delphi заключается в одновременном обновлении сторонних библиотек. Если бы вы сделали это, то, я думаю, вам не нужно было бы переносить 15-летний библиотечный код.

Другие вопросы по тегам