Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting Turbo Pascal inline code to Object Pascal

While converting old Turbo Pascal units to modern Object Pascal, I ran into the following:

function Less (var a, b; Relation : POINTER) : boolean;
    inline($5B/$59/$0E/$E8/$00/$00/$58/$05/$08/$00/$50/$51/$53/$CB);

The code is supposed to call an external function {$F+} function VariableLess(var a, b : Index) : boolean; {$F-}, collect the result and pass it to the calling function. The function is used in a unit that provides binary trees for untyped data

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. }

Therefore, the unit itself cannot provide a comparison function, that is the job of the unit that defines the payload.

Using an online disassembler I found out that this corresponds to:

{$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;

However, the compiler doesn't like the push statement. What should I do to get this to work on a modern 64-bit machine? I realise the code is 16-bit.

like image 338
Engelbert Buxbaum Avatar asked Dec 23 '22 16:12

Engelbert Buxbaum


1 Answers

I just compiled some inline function on Turbo Pascal 5 for MS-DOS to check how Turbo Pascal generates code:

For non-inline function calls, Turbo Pascal pushes all function arguments to the stack. The first one is pushed first (so SS:SP points to the last function argument). Then a (far) call is executed. The function returns using retf n, which means that the function called removes all parameters from the stack.

In an inline function, the raw bytes given simply replace the call instruction. This means that SS:SP points to the arguments, not to the return address. The inline machine language code must pop the arguments from the stack. And it must not return using ret but simply continue code execution at the instruction after the inline code.

With this knowledge the assembly code can be analyzed:

Using the assembly code given, you can call any function or procedure with any parameters (in your case: VariableLess) indirectly by writing a helper function (in your case: Less) that has the same arguments as the function to be called plus an additional argument that points to the actual function.

The code is equal to the following Delphi or FreePascal code:

type
    TMyCompare = function(var a, b) : boolean;

function Less (var a, b; Relation : TMyCompare) : boolean;
begin
    Less := Relation(a, b);
end;

If your compiler supports function pointers (type TMyCompare = function ...), you could do it like this.

Or you could even replace all occurrences of Less(x,y,z) in your program by z(x,y). This would even be more efficient.

Of course, the pointer to the function (VariableLess) should not have the type pointer but the type TMyCompare if you do it like this.

If your compiler does not support function pointers (as Turbo Pascal obviously did not), you might need assembly.

But in that case, different compilers will need different assembly code!

So not knowing internals of your compiler, it is not possible to translate the assembly code.

EDIT

I'm not sure how exactly your compiler works. However, maybe the following code works if my original code does not work:

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;
like image 121
Martin Rosenau Avatar answered Dec 28 '22 07:12

Martin Rosenau