mirror of
https://github.com/moparisthebest/Simba
synced 2024-11-26 11:12:14 -05:00
514 lines
13 KiB
PHP
514 lines
13 KiB
PHP
|
{ implementation of x64 abi }
|
||
|
//procedure DebugBreak; external 'Kernel32.dll';
|
||
|
const
|
||
|
EmptyPchar: array[0..0] of char = #0;
|
||
|
{$ASMMODE INTEL}
|
||
|
|
||
|
{$IFDEF WINDOWS}
|
||
|
procedure x64call(
|
||
|
Address: Pointer;
|
||
|
out _RAX: IPointer;
|
||
|
_RCX, _RDX, _R8, _R9: IPointer;
|
||
|
var _XMM0: Double;
|
||
|
_XMM1, _XMM2, _XMM3: Double;
|
||
|
aStack: Pointer; aItems: Integer); assembler; nostackframe;
|
||
|
asm
|
||
|
(* Registers:
|
||
|
RCX: Address
|
||
|
RDX: *_RAX
|
||
|
R8: _RCX
|
||
|
R9: _RDX
|
||
|
|
||
|
fpc inserts an 20h emty space
|
||
|
*)
|
||
|
push ebp
|
||
|
mov ebp,esp
|
||
|
// call debugbreak
|
||
|
push rcx // address
|
||
|
push rdx // _rax
|
||
|
push r8 // _rcx
|
||
|
push r9 // _rdx
|
||
|
mov rcx, aItems
|
||
|
mov rdx, aStack
|
||
|
jmp @compareitems
|
||
|
@work:
|
||
|
push [rdx]
|
||
|
dec rcx
|
||
|
sub rdx,8
|
||
|
@compareitems:
|
||
|
or rcx, rcx
|
||
|
jnz @work
|
||
|
|
||
|
// copy registers
|
||
|
movd xmm0,[_XMM0]
|
||
|
movd xmm1,_XMM1
|
||
|
movd xmm2,_XMM2
|
||
|
movd xmm3,_XMM3
|
||
|
mov RAX, [rbp-8]
|
||
|
mov RCX, [rbp-24]
|
||
|
mov RDX, [rbp-32]
|
||
|
mov R8, _R8
|
||
|
mov R9, _R9
|
||
|
|
||
|
// weird thing on windows, it needs 32 bytes in the CALLEE side to do whatever in
|
||
|
sub RSP, 32
|
||
|
|
||
|
call RAX
|
||
|
|
||
|
add RSP, 32 // undo the damage done earlier
|
||
|
|
||
|
// copy result back
|
||
|
mov RDX, [rbp-16]
|
||
|
mov [RDX], RAX
|
||
|
movd [_XMM0],xmm0
|
||
|
|
||
|
pop r9
|
||
|
pop r8
|
||
|
pop rdx
|
||
|
pop rcx
|
||
|
leave
|
||
|
ret
|
||
|
end;
|
||
|
{$ELSE}
|
||
|
procedure x64call(
|
||
|
Address: Pointer;
|
||
|
out _RAX: IPointer;
|
||
|
_RDI, _RSI, _RDX, _RCX, _R8, _R9: IPointer;
|
||
|
var _XMM0: Double;
|
||
|
_XMM1, _XMM2, _XMM3, _XMM4, _XMM5, _XMM6, _XMM7: Double;
|
||
|
aStack: Pointer; aItems: Integer); assembler; nostackframe;
|
||
|
|
||
|
|
||
|
asm
|
||
|
(* Registers:
|
||
|
RDI: Address
|
||
|
RSI: _RAX
|
||
|
RDX: _RDI
|
||
|
RCX: _RSI
|
||
|
R8: _RDX
|
||
|
R9: _RCX
|
||
|
|
||
|
|
||
|
*)
|
||
|
push ebp
|
||
|
mov ebp,esp
|
||
|
push rdi // address
|
||
|
push rsi // _rax
|
||
|
push rdx // _rdi
|
||
|
push rcx // _rsi
|
||
|
push r8 // _rdx
|
||
|
push r9 // _rcx
|
||
|
mov rcx, aItems
|
||
|
mov rdx, aStack
|
||
|
jmp @compareitems
|
||
|
@work:
|
||
|
push [rdx]
|
||
|
dec rcx
|
||
|
sub rdx,8
|
||
|
@compareitems:
|
||
|
or rcx, rcx
|
||
|
jnz @work
|
||
|
|
||
|
// copy registers
|
||
|
movd xmm0,[_XMM0]
|
||
|
movd xmm1,_XMM1
|
||
|
movd xmm2,_XMM2
|
||
|
movd xmm3,_XMM3
|
||
|
movd xmm4,_XMM4
|
||
|
movd xmm5,_XMM5
|
||
|
movd xmm6,_XMM6
|
||
|
movd xmm7,_XMM7
|
||
|
mov RAX, [rbp-8]
|
||
|
mov RDI, [rbp-24]
|
||
|
mov RSI, [rbp-32]
|
||
|
mov RDX, [rbp-40]
|
||
|
mov RCX, [rbp-48]
|
||
|
mov R8, _R8
|
||
|
mov R9, _R9
|
||
|
|
||
|
// weird thing on windows, it needs 32 bytes in the CALLEE side to do whatever in; not sure about linux
|
||
|
//sub RSP, 32
|
||
|
|
||
|
call RAX
|
||
|
|
||
|
// add RSP, 32 // undo the damage done earlier
|
||
|
|
||
|
// copy result back
|
||
|
mov RDX, [rbp-16]
|
||
|
mov [RDX], RAX
|
||
|
movd [_XMM0],xmm0
|
||
|
|
||
|
pop r9
|
||
|
pop r8
|
||
|
pop rdx
|
||
|
pop rcx
|
||
|
pop rsi
|
||
|
pop rdi
|
||
|
leave
|
||
|
ret
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
|
||
|
function TPSExec.InnerfuseCall(_Self, Address: Pointer; CallingConv: TPSCallingConvention; Params: TPSList; res: PPSVariantIFC): Boolean;
|
||
|
var
|
||
|
Stack: array of Byte;
|
||
|
_RAX,
|
||
|
{$IFDEF WINDOWS}
|
||
|
_RCX, _RDX, _R8, _R9: IPointer;
|
||
|
_XMM0, _XMM1, _XMM2, _XMM3: Double;
|
||
|
{$ELSE}
|
||
|
_RDI, _RSI, _RDX, _RCX, _R8, _R9: IPointer;
|
||
|
_XMM0, _XMM1, _XMM2, _XMM3, _XMM4, _XMM5, _XMM6, _XMM7: Double;
|
||
|
RegUsageFloat: Byte;
|
||
|
{$ENDIF}
|
||
|
RegUsage: Byte;
|
||
|
CallData: TPSList;
|
||
|
I: Integer;
|
||
|
pp: ^Byte;
|
||
|
|
||
|
function rp(p: PPSVariantIFC): PPSVariantIFC;
|
||
|
begin
|
||
|
if p = nil then
|
||
|
begin
|
||
|
result := nil;
|
||
|
exit;
|
||
|
end;
|
||
|
if p.aType.BaseType = btPointer then
|
||
|
begin
|
||
|
p^.aType := Pointer(Pointer(IPointer(p^.dta) + PointerSize)^);
|
||
|
p^.Dta := Pointer(p^.dta^);
|
||
|
end;
|
||
|
Result := p;
|
||
|
end;
|
||
|
{$IFDEF WINDOWS}
|
||
|
procedure StoreReg(data: IPointer); overload;
|
||
|
var p: Pointer;
|
||
|
begin
|
||
|
case RegUsage of
|
||
|
0: begin inc(RegUsage); _RCX:=Data; end;
|
||
|
1: begin inc(RegUsage); _RDX:=Data; end;
|
||
|
2: begin inc(RegUsage); _R8:=Data; end;
|
||
|
3: begin inc(RegUsage); _R9:=Data; end;
|
||
|
else begin
|
||
|
SetLength(Stack, Length(Stack)+8);
|
||
|
p := @Stack[LEngth(Stack)-8];
|
||
|
IPointer(p^) := data;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
{$ELSE}
|
||
|
procedure StoreReg(data: IPointer); overload;
|
||
|
var p: Pointer;
|
||
|
begin
|
||
|
case RegUsage of
|
||
|
0: begin inc(RegUsage); _RDI:=Data; end;
|
||
|
1: begin inc(RegUsage); _RSI:=Data; end;
|
||
|
2: begin inc(RegUsage); _RDX:=Data; end;
|
||
|
3: begin inc(RegUsage); _RCX:=Data; end;
|
||
|
4: begin inc(RegUsage); _R8:=Data; end;
|
||
|
5: begin inc(RegUsage); _R9:=Data; end;
|
||
|
else begin
|
||
|
SetLength(Stack, Length(Stack)+8);
|
||
|
p := @Stack[LEngth(Stack)-8];
|
||
|
IPointer(p^) := data;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
|
||
|
procedure StoreStack(const aData; Len: Integer);
|
||
|
var
|
||
|
p: Pointer;
|
||
|
begin
|
||
|
if Len > 8 then
|
||
|
if Length(Stack) mod 16 <> 0 then begin
|
||
|
SetLength(Stack, Length(Stack)+ (16-(Length(Stack) mod 16)));
|
||
|
end;
|
||
|
SetLength(Stack, Length(Stack)+Len);
|
||
|
p := @Stack[Length(Stack)-Len];
|
||
|
Move(aData, p^, Len);
|
||
|
end;
|
||
|
|
||
|
{$IFDEF WINDOWS}
|
||
|
procedure StoreReg(data: Double); overload;
|
||
|
var p: Pointer;
|
||
|
begin
|
||
|
case RegUsage of
|
||
|
0: begin inc(RegUsage); _XMM0:=Data; end;
|
||
|
1: begin inc(RegUsage); _XMM1:=Data; end;
|
||
|
2: begin inc(RegUsage); _XMM2:=Data; end;
|
||
|
3: begin inc(RegUsage); _XMM3:=Data; end;
|
||
|
else begin
|
||
|
SetLength(Stack, Length(Stack)+8);
|
||
|
p := @Stack[LEngth(Stack)-8];
|
||
|
Double(p^) := data;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
{$ELSE}
|
||
|
procedure StoreReg(data: Double); overload;
|
||
|
var p: Pointer;
|
||
|
begin
|
||
|
case RegUsageFloat of
|
||
|
0: begin inc(RegUsageFloat); _XMM0:=Data; end;
|
||
|
1: begin inc(RegUsageFloat); _XMM1:=Data; end;
|
||
|
2: begin inc(RegUsageFloat); _XMM2:=Data; end;
|
||
|
3: begin inc(RegUsageFloat); _XMM3:=Data; end;
|
||
|
4: begin inc(RegUsageFloat); _XMM4:=Data; end;
|
||
|
5: begin inc(RegUsageFloat); _XMM5:=Data; end;
|
||
|
6: begin inc(RegUsageFloat); _XMM6:=Data; end;
|
||
|
7: begin inc(RegUsageFloat); _XMM7:=Data; end;
|
||
|
else begin
|
||
|
SetLength(Stack, Length(Stack)+8);
|
||
|
p := @Stack[LEngth(Stack)-8];
|
||
|
Double(p^) := data;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
function GetPtr(fVar: PPSVariantIFC): Boolean;
|
||
|
var
|
||
|
varPtr: Pointer;
|
||
|
//UseReg: Boolean;
|
||
|
//tempstr: tbtstring;
|
||
|
p: Pointer;
|
||
|
begin
|
||
|
Result := False;
|
||
|
if FVar = nil then exit;
|
||
|
if fVar.VarParam then
|
||
|
begin
|
||
|
case fvar.aType.BaseType of
|
||
|
btArray:
|
||
|
begin
|
||
|
if Copy(fvar.aType.ExportName, 1, 10) = '!OPENARRAY' then
|
||
|
begin
|
||
|
p := CreateOpenArray(True, Self, FVar);
|
||
|
if p = nil then exit;
|
||
|
CallData.Add(p);
|
||
|
StoreReg(IPointer(POpenArray(p)^.Data));
|
||
|
StoreReg(IPointer(POpenArray(p)^.ItemCount -1));
|
||
|
Result := True;
|
||
|
Exit;
|
||
|
end else begin
|
||
|
varptr := fvar.Dta;
|
||
|
// Exit;
|
||
|
end;
|
||
|
end;
|
||
|
btVariant,
|
||
|
btSet,
|
||
|
btStaticArray,
|
||
|
btRecord,
|
||
|
btInterface,
|
||
|
btClass,
|
||
|
{$IFNDEF PS_NOWIDESTRING} btUnicodeString, btWideString, btWideChar, {$ENDIF} btU8, btS8, btU16,
|
||
|
btS16, btU32, btS32, btSingle, btDouble, btExtended, btString, btPChar, btChar, btCurrency
|
||
|
{$IFNDEF PS_NOINT64}, bts64{$ENDIF}:
|
||
|
begin
|
||
|
Varptr := fvar.Dta;
|
||
|
end;
|
||
|
else begin
|
||
|
exit; //invalid type
|
||
|
end;
|
||
|
end; {case}
|
||
|
|
||
|
StoreReg(IPointer(VarPtr));
|
||
|
end else begin
|
||
|
// UseReg := True;
|
||
|
case fVar^.aType.BaseType of
|
||
|
btSet:
|
||
|
begin
|
||
|
case TPSTypeRec_Set(fvar.aType).aByteSize of
|
||
|
1: StoreReg(IPointer(byte(fvar.dta^)));
|
||
|
2: StoreReg(IPointer(word(fvar.dta^)));
|
||
|
3, 4: StoreReg(IPointer(cardinal(fvar.dta^)));
|
||
|
5,6,7,8: StoreReg(IPointer(fVar.Dta^));
|
||
|
else
|
||
|
StoreReg(IPointer(fvar.Dta));
|
||
|
end;
|
||
|
end;
|
||
|
btArray:
|
||
|
begin
|
||
|
if Copy(fvar^.aType.ExportName, 1, 10) = '!OPENARRAY' then
|
||
|
begin
|
||
|
p := CreateOpenArray(False, SElf, FVar);
|
||
|
if p =nil then exit;
|
||
|
CallData.Add(p);
|
||
|
StoreReg(IPointer(POpenArray(p)^.Data));
|
||
|
StoreReg(IPointer(POpenArray(p)^.ItemCount -1));
|
||
|
Result := True;
|
||
|
exit;
|
||
|
end else begin
|
||
|
StoreReg(IPointer(FVar.Dta^));
|
||
|
end;
|
||
|
end;
|
||
|
btVariant
|
||
|
, btStaticArray, btRecord:
|
||
|
begin
|
||
|
StoreReg(IPointer(fVar.Dta));
|
||
|
end;
|
||
|
btExtended, btDouble: {8 bytes} begin
|
||
|
StoreReg(double(fvar.dta^));
|
||
|
end;
|
||
|
btCurrency: {8 bytes} begin
|
||
|
StoreReg(IPointer(fvar.dta^));
|
||
|
end;
|
||
|
btSingle: {4 bytes} begin
|
||
|
StoreReg(single(fvar.dta^));
|
||
|
end;
|
||
|
|
||
|
btChar,
|
||
|
btU8,
|
||
|
btS8: begin
|
||
|
StoreReg(IPointer(byte(fVar^.dta^)));
|
||
|
end;
|
||
|
btWideChar,
|
||
|
btu16, btS16: begin
|
||
|
StoreReg(IPointer(word(fVar^.dta^)));
|
||
|
end;
|
||
|
btu32, bts32: begin
|
||
|
StoreReg(IPointer(cardinal(fVar^.dta^)));
|
||
|
end;
|
||
|
btPchar:
|
||
|
begin
|
||
|
if pointer(fvar^.dta^) = nil then
|
||
|
StoreReg(IPointer(@EmptyPchar))
|
||
|
else
|
||
|
StoreReg(IPointer(fvar^.dta^));
|
||
|
end;
|
||
|
btclass, btinterface, btString:
|
||
|
begin
|
||
|
StoreReg(IPointer(fvar^.dta^));
|
||
|
end;
|
||
|
btWideString: begin
|
||
|
StoreReg(IPointer(fvar^.dta^));
|
||
|
end;
|
||
|
btUnicodeString: begin
|
||
|
StoreReg(IPointer(fvar^.dta^));
|
||
|
end;
|
||
|
|
||
|
btProcPtr:
|
||
|
begin
|
||
|
GetMem(p, PointerSize2);
|
||
|
TMethod(p^) := MKMethod(Self, Longint(FVar.Dta^));
|
||
|
StoreStack(p^, Pointersize2);
|
||
|
FreeMem(p);
|
||
|
end;
|
||
|
|
||
|
bts64:
|
||
|
begin
|
||
|
StoreReg(IPointer(int64(fvar^.dta^)));
|
||
|
end;
|
||
|
end; {case}
|
||
|
end;
|
||
|
Result := True;
|
||
|
end;
|
||
|
begin
|
||
|
InnerfuseCall := False;
|
||
|
if Address = nil then
|
||
|
exit; // need address
|
||
|
SetLength(Stack, 0);
|
||
|
CallData := TPSList.Create;
|
||
|
res := rp(res);
|
||
|
if res <> nil then
|
||
|
res.VarParam := true;
|
||
|
try
|
||
|
{$IFNDEF WINDOWS}
|
||
|
_RSI := 0;
|
||
|
_RDI := 0;
|
||
|
_XMM4 := 0;
|
||
|
_XMM5 := 0;
|
||
|
_XMM6 := 0;
|
||
|
_XMM7 := 0;
|
||
|
RegUsageFloat := 0;
|
||
|
{$ENDIF}
|
||
|
_RCX := 0;
|
||
|
_RDX := 0;
|
||
|
_R8 := 0;
|
||
|
_R9 := 0;
|
||
|
_XMM0 := 0;
|
||
|
_XMM1 := 0;
|
||
|
_XMM2 := 0;
|
||
|
_XMM3 := 0;
|
||
|
RegUsage := 0;
|
||
|
if assigned(_Self) then begin
|
||
|
RegUsage := 1;
|
||
|
_RCX := IPointer(_Self);
|
||
|
end;
|
||
|
for I := 0 to Params.Count - 1 do
|
||
|
begin
|
||
|
if not GetPtr(rp(Params[I])) then Exit;
|
||
|
end;
|
||
|
|
||
|
if assigned(res) then begin
|
||
|
case res^.aType.BaseType of
|
||
|
{$IFDEF x64_string_result_as_varparameter}
|
||
|
btstring, btWideString, btUnicodeString,
|
||
|
{$ENDIF}
|
||
|
btInterface, btArray, btrecord, btVariant, btStaticArray: GetPtr(res);
|
||
|
btSet:
|
||
|
begin
|
||
|
if TPSTypeRec_Set(res.aType).aByteSize > PointerSize then GetPtr(res);
|
||
|
end;
|
||
|
end;
|
||
|
if Stack = nil then pp := nil else pp := @Stack[Length(Stack) -8];
|
||
|
{$IFDEF WINDOWS}
|
||
|
x64call(Address, _RAX, _RCX, _RDX, _R8, _R9, _XMM0, _XMM1, _XMM2, _XMM3, pp, Length(Stack) div 8);
|
||
|
{$ELSE}
|
||
|
x64call(Address, _RAX, _RDI, _RSI, _RDX, _RCX, _R8, _R9,_XMM0,_XMM1, _XMM2, _XMM3, _XMM4, _XMM5, _XMM6, _XMM7, pp, Length(Stack) div 8);
|
||
|
{$ENDIF}
|
||
|
case res^.aType.BaseType of
|
||
|
btSet:
|
||
|
begin
|
||
|
case TPSTypeRec_Set(res.aType).aByteSize of
|
||
|
1: byte(res.Dta^) := _RAX;
|
||
|
2: word(res.Dta^) := _RAX;
|
||
|
3,
|
||
|
4: Longint(res.Dta^) := _RAX;
|
||
|
5,6,7,8: IPointer(res.dta^) := _RAX;
|
||
|
end;
|
||
|
end;
|
||
|
btSingle: tbtsingle(res.Dta^) := _XMM0;
|
||
|
btDouble: tbtdouble(res.Dta^) := _XMM0;
|
||
|
btExtended: tbtextended(res.Dta^) := _XMM0;
|
||
|
btchar,btU8, btS8: tbtu8(res.dta^) := _RAX;
|
||
|
btWideChar, btu16, bts16: tbtu16(res.dta^) := _RAX;
|
||
|
btClass : IPointer(res.dta^) := _RAX;
|
||
|
btu32,bts32: tbtu32(res.dta^) := _RAX;
|
||
|
btPChar: pansichar(res.dta^) := Pansichar(_RAX);
|
||
|
bts64: tbts64(res.dta^) := Int64(_RAX);
|
||
|
btCurrency: tbtCurrency(res.Dta^) := Int64(_RAX);
|
||
|
btInterface,
|
||
|
btVariant,
|
||
|
{$IFDEF x64_string_result_as_varparameter}
|
||
|
btWidestring,btUnicodestring, btstring ,
|
||
|
{$ENDIF}
|
||
|
btStaticArray, btArray, btrecord:;
|
||
|
{$IFNDEF x64_string_result_as_varparameter}
|
||
|
btUnicodeString, btWideString, btstring: Int64(res.dta^) := _RAX;
|
||
|
{$ENDIF}
|
||
|
else
|
||
|
exit;
|
||
|
end;
|
||
|
end else begin
|
||
|
if Stack = nil then pp := nil else pp := @Stack[Length(Stack) -8];
|
||
|
{$IFDEF WINDOWS}
|
||
|
x64call(Address, _RAX, _RCX, _RDX, _R8, _R9, _XMM0, _XMM1, _XMM2, _XMM3, pp, Length(Stack) div 8);
|
||
|
{$ELSE}
|
||
|
x64call(Address, _RAX, _RDI, _RSI, _RDX, _RCX, _R8, _R9,_XMM0,_XMM1, _XMM2, _XMM3, _XMM4, _XMM5, _XMM6, _XMM7, pp, Length(Stack) div 8);
|
||
|
{$ENDIF}
|
||
|
end;
|
||
|
Result := True;
|
||
|
finally
|
||
|
for i := CallData.Count -1 downto 0 do
|
||
|
begin
|
||
|
pp := CallData[i];
|
||
|
case pp^ of
|
||
|
0: DestroyOpenArray(Self, Pointer(pp));
|
||
|
end;
|
||
|
end;
|
||
|
CallData.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
|