Delphi asm to purepascal

1.3k Views Asked by At

I am trying to migrate Delphi 5 code to Delphi XE7-WIN64 and I am facing a problem with mixed assembly code in the following block. Also I am asm newbie.

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;

I am trying to convert the asm block to purepascal since delphi x64 doesn't allow mixed code and asm is not programmer-friendly.

As far I understand address of Item is moved to EAX, Then I am not getting anything. Where did EBP come from? What is ESP and AL? The above code snippet is from ELmenus.pas from ELPack.

So basically what will be the PurePascal version of the asm codeblock?

For func I found this

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

There are 2 best solutions below

34
On

That asm code is calling a method on Item. I'd say that whoever wrote the code needs their head examined. It's as if they don't know about method pointers.

The way I would tackle this is to look for the code that calls the function and see what is passed as the Func argument. That's what is being called by the asm block. Change the type of the Func argument to an appropriate procedural type and replace the asm block with a call to that method pointer.


OK, now I see that the original code is playing fast and loose with local procedures. What you should do is make Func be an anonymous method type:

Func: TFunc<TElMenuItem, Boolean>;

Then convert the local functions to be anonymous methods.


The usual approach to upgrading Delphi versions is to upgrade the third party libraries at the same time. Were you to do this then I guess you would not need to port the 15 year old library code.

0
On

I'm guessing the original programmer wrote the strange code to bypass Delphi not supporting local function pointers. (Which is a limitation that has annoyed me on occasion.)

The following should work. (Though I'm not that sharp on my asm to be certain. I would first test the asm code by stepping through the debugger in D5 to confirm that the line CALL DWORD PTR AFunc is passing the values I expect.)

1) Delcare a function pointer type and move the UpdateItem so that it's no longer local.

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) Make the following changes:

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) And finally the assembler block becomes:

Result := AFunc(Item);