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.
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With