Delphi XE2:Win64プラットフォームのASMメソッドを変換する
-
28-10-2019 - |
質問
Win64プラットフォームのPascalユニットをコンパイルしようとすると、エラーが発生します。メソッドにはASMブロックが含まれています。 Win64プラットフォームで機能する方法はありません。
方法1:
Function TSparseList.ForAll( ApplyFunction: Pointer {TSPAApply} ): Integer; Assembler;
Asm
MOV EAX,[EAX].TSparseList.FList
JMP TSparsePointerArray.ForAll
End;
方法2:
Function TSparsePointerArray.ForAll( ApplyFunction: Pointer {TSPAApply} ):
Integer;
Var
itemP: PAnsiChar; { Pointer to item in section } { patched by ccy }
item: Pointer;
i, callerBP: Cardinal;
j, index: Integer;
Begin
{ Scan section directory and scan each section that exists,
calling the apply function for each non-nil item.
The apply function must be a far local function in the scope of
the procedure P calling ForAll. The trick of setting up the stack
frame (taken from TurboVision's TCollection.ForEach) allows the
apply function access to P's arguments and local variables and,
if P is a method, the instance variables and methods of P's class '}
Result := 0;
i := 0;
Asm
mov eax,[ebp] { Set up stack frame for local }
mov callerBP,eax
End;
While ( i < slotsInDir ) And ( Result = 0 ) Do
Begin
itemP := secDir^[i];
If itemP <> Nil Then
Begin
j := 0;
index := i Shl SecShift;
While ( j < FSectionSize ) And ( Result = 0 ) Do
Begin
item := PPointer( itemP )^;
If item <> Nil Then
{ ret := ApplyFunction(index, item.Ptr); }
Asm
mov eax,index
mov edx,item
push callerBP
call ApplyFunction
pop ecx
mov @Result,eax
End;
Inc( itemP, SizeOf( Pointer ) );
Inc( j );
Inc( index )
End
End;
Inc( i )
End;
End;
解決
私はX64の指示の詳細に精通していないので、64ビットをサポートするためにアセンブリコードを書き換えるのを手伝うことはできませんが、Embarcaderoの64ビットコンパイラを伝えることができます ではない 現在、同じ機能でPascalとAssemblyを混ぜることができます。すべてのパスカルまたは全アセンブリ機能のみを記述することができますが、混合はまったくありません(パスカル関数はアセンブリ関数を呼び出すことができ、その逆も同様ですが、x86のように一緒に共存することはできません)。したがって、メソッドを書き直す必要があります。
他のヒント
X64 ASMでメソッド全体を書き換えることができる場合があります。レミーが言ったように、あなたはいくつかをネストすることができないので、あなたは方法全体を書き直す必要があります asm .. end
内部のブロック begin .. end
.
本当の問題は、慣習を呼び出すことはWin32およびWin64モードでは同じではないということです。レジスタの変更(つまり、それらは64ビットであり、現在はSSE2レジスタを含むものとします)が、主な問題は、コールリネジェクターのパラメーターの数を知る必要があるという事実に関するものです。
もしあなたの TSPAApply
関数には多くの固定パラメーターがあり、それをプレーンパスカルバージョンに変換することができます - これはすべてよりも安全です。
type
TSPAApply = function(index: integer; item: pointer);
Function TSparseList.ForAll( ApplyFunction: Pointer {TSPAApply} ): Integer;
begin
result := FList.ForAll(ApplyFunction);
End;
Function TSparsePointerArray.ForAll( ApplyFunction: Pointer {TSPAApply} ):
Integer;
Var
itemP: PPointer;
i: Cardinal;
j, index: Integer;
Begin
Result := 0;
i := 0;
While ( i < slotsInDir ) And ( Result = 0 ) Do
Begin
itemP := secDir^[i];
If itemP <> Nil Then
Begin
j := 0;
index := i Shl SecShift;
While ( j < FSectionSize ) And ( Result = 0 ) Do
Begin
If itemP^ <> Nil Then
result := TSPAApply(ApplyFunction)(index,itemP^.Ptr);
Inc( itemP );
Inc( j );
Inc( index )
End
End;
Inc( i )
End;
End;
しかし、あなたはよりよく頼るべきです TMethod
リスト、それを行うより一般的なOOPの方法。ここでは、いくつかのコードリファクタリングが良い考えです。
試す
Function TSparseList.ForAll( ApplyFunction: Pointer {TSPAApply} ): Integer; Assembler;
Asm
MOV RAX,[RAX].TSparseList.FList
JMP TSparsePointerArray.ForAll
End;
ポインターはx64で64ビットであるため、64ビットの完全なレジスタが占められます。 「A」レジスタは、それぞれ8/16/32/64ビットのAL/AX/EAX/RAXです。
2番目の関数については、ASMブロックで呼び出されている関数についてもっと知る必要があります。