git-svn-id: https://svn.code.sf.net/p/kolmck/code@17 91bb2d04-0c0c-4d2d-88a5-bbb6f4c1fa07
19074 lines
505 KiB
ObjectPascal
19074 lines
505 KiB
ObjectPascal
{ *********************************************************************** }
|
|
{ }
|
|
{ Delphi / Kylix Cross-Platform Runtime Library }
|
|
{ System Unit }
|
|
{ }
|
|
{ Copyright (c) 1988-2002 Borland Software Corporation }
|
|
{ }
|
|
{ *********************************************************************** }
|
|
|
|
//Avenger SysDcu for Delphi 7
|
|
|
|
unit System; { Predefined constants, types, procedures, }
|
|
{ and functions (such as True, Integer, or }
|
|
{ Writeln) do not have actual declarations.}
|
|
{ Instead they are built into the compiler }
|
|
{ and are treated as if they were declared }
|
|
{ at the beginning of the System unit. }
|
|
|
|
{$H+,I-,R-,O+,W-}
|
|
{$WARN SYMBOL_PLATFORM OFF}
|
|
|
|
{ L- should never be specified.
|
|
|
|
The IDE needs to find DebugHook (through the C++
|
|
compiler sometimes) for integrated debugging to
|
|
function properly.
|
|
|
|
ILINK will generate debug info for DebugHook if
|
|
the object module has not been compiled with debug info.
|
|
|
|
ILINK will not generate debug info for DebugHook if
|
|
the object module has been compiled with debug info.
|
|
|
|
Thus, the Pascal compiler must be responsible for
|
|
generating the debug information for that symbol
|
|
when a debug-enabled object file is produced.
|
|
}
|
|
|
|
interface
|
|
|
|
(* You can use RTLVersion in $IF expressions to test the runtime library
|
|
version level independently of the compiler version level.
|
|
Example: {$IF RTLVersion >= 16.2} ... {$IFEND} *)
|
|
|
|
const
|
|
RTLVersion = 14.1;
|
|
|
|
{$EXTERNALSYM CompilerVersion}
|
|
|
|
(*
|
|
const
|
|
CompilerVersion = 0.0;
|
|
|
|
CompilerVersion is assigned a value by the compiler when
|
|
the system unit is compiled. It indicates the revision level of the
|
|
compiler features / language syntax, which may advance independently of
|
|
the RTLVersion. CompilerVersion can be tested in $IF expressions and
|
|
should be used instead of testing for the VERxxx conditional define.
|
|
Always test for greater than or less than a known revision level.
|
|
It's a bad idea to test for a specific revision level.
|
|
*)
|
|
|
|
|
|
{$IFDEF DECLARE_GPL}
|
|
(* The existence of the GPL symbol indicates that the System unit
|
|
and the rest of the Delphi runtime library were compiled for use
|
|
and distribution under the terms of the GNU General Public License (GPL).
|
|
Under the terms of the GPL, all applications compiled with the
|
|
GPL version of the Delphi runtime library must also be distributed
|
|
under the terms of the GPL.
|
|
For more information about the GNU GPL, see
|
|
http://www.gnu.org/copyleft/gpl.html
|
|
|
|
The GPL symbol does not exist in the Delphi runtime library
|
|
purchased for commercial/proprietary software development.
|
|
|
|
If your source code needs to know which licensing model it is being
|
|
compiled into, you can use {$IF DECLARED(GPL)}...{$IFEND} to
|
|
test for the existence of the GPL symbol. The value of the
|
|
symbol itself is not significant. *)
|
|
|
|
const
|
|
GPL = True;
|
|
{$ENDIF}
|
|
|
|
{ Variant type codes (wtypes.h) }
|
|
|
|
varEmpty = $0000; { vt_empty }
|
|
varNull = $0001; { vt_null }
|
|
varSmallint = $0002; { vt_i2 }
|
|
varInteger = $0003; { vt_i4 }
|
|
varSingle = $0004; { vt_r4 }
|
|
varDouble = $0005; { vt_r8 }
|
|
varCurrency = $0006; { vt_cy }
|
|
varDate = $0007; { vt_date }
|
|
varOleStr = $0008; { vt_bstr }
|
|
varDispatch = $0009; { vt_dispatch }
|
|
varError = $000A; { vt_error }
|
|
varBoolean = $000B; { vt_bool }
|
|
varVariant = $000C; { vt_variant }
|
|
varUnknown = $000D; { vt_unknown }
|
|
//varDecimal = $000E; { vt_decimal } {UNSUPPORTED}
|
|
{ undefined $0f } {UNSUPPORTED}
|
|
varShortInt = $0010; { vt_i1 }
|
|
varByte = $0011; { vt_ui1 }
|
|
varWord = $0012; { vt_ui2 }
|
|
varLongWord = $0013; { vt_ui4 }
|
|
varInt64 = $0014; { vt_i8 }
|
|
//varWord64 = $0015; { vt_ui8 } {UNSUPPORTED}
|
|
|
|
{ if adding new items, update Variants' varLast, BaseTypeMap and OpTypeMap }
|
|
varStrArg = $0048; { vt_clsid }
|
|
varString = $0100; { Pascal string; not OLE compatible }
|
|
varAny = $0101; { Corba any }
|
|
varTypeMask = $0FFF;
|
|
varArray = $2000;
|
|
varByRef = $4000;
|
|
|
|
{ TVarRec.VType values }
|
|
|
|
vtInteger = 0;
|
|
vtBoolean = 1;
|
|
vtChar = 2;
|
|
vtExtended = 3;
|
|
vtString = 4;
|
|
vtPointer = 5;
|
|
vtPChar = 6;
|
|
vtObject = 7;
|
|
vtClass = 8;
|
|
vtWideChar = 9;
|
|
vtPWideChar = 10;
|
|
vtAnsiString = 11;
|
|
vtCurrency = 12;
|
|
vtVariant = 13;
|
|
vtInterface = 14;
|
|
vtWideString = 15;
|
|
vtInt64 = 16;
|
|
|
|
{ Virtual method table entries }
|
|
|
|
vmtSelfPtr = -76;
|
|
vmtIntfTable = -72;
|
|
vmtAutoTable = -68;
|
|
vmtInitTable = -64;
|
|
vmtTypeInfo = -60;
|
|
vmtFieldTable = -56;
|
|
vmtMethodTable = -52;
|
|
vmtDynamicTable = -48;
|
|
vmtClassName = -44;
|
|
vmtInstanceSize = -40;
|
|
vmtParent = -36;
|
|
vmtSafeCallException = -32;
|
|
vmtAfterConstruction = -28;
|
|
vmtBeforeDestruction = -24;
|
|
vmtDispatch = -20;
|
|
vmtDefaultHandler = -16;
|
|
vmtNewInstance = -12;
|
|
vmtFreeInstance = -8;
|
|
vmtDestroy = -4;
|
|
|
|
vmtQueryInterface = 0;
|
|
vmtAddRef = 4;
|
|
vmtRelease = 8;
|
|
vmtCreateObject = 12;
|
|
|
|
type
|
|
|
|
TObject = class;
|
|
|
|
TClass = class of TObject;
|
|
|
|
HRESULT = type Longint; { from WTYPES.H }
|
|
{$EXTERNALSYM HRESULT}
|
|
|
|
PGUID = ^TGUID;
|
|
TGUID = packed record
|
|
D1: LongWord;
|
|
D2: Word;
|
|
D3: Word;
|
|
D4: array[0..7] of Byte;
|
|
end;
|
|
|
|
PInterfaceEntry = ^TInterfaceEntry;
|
|
TInterfaceEntry = packed record
|
|
IID: TGUID;
|
|
VTable: Pointer;
|
|
IOffset: Integer;
|
|
ImplGetter: Integer;
|
|
end;
|
|
|
|
PInterfaceTable = ^TInterfaceTable;
|
|
TInterfaceTable = packed record
|
|
EntryCount: Integer;
|
|
Entries: array[0..9999] of TInterfaceEntry;
|
|
end;
|
|
|
|
TMethod = record
|
|
Code, Data: Pointer;
|
|
end;
|
|
|
|
{ TObject.Dispatch accepts any data type as its Message parameter. The
|
|
first 2 bytes of the data are taken as the message id to search for
|
|
in the object's message methods. TDispatchMessage is an example of
|
|
such a structure with a word field for the message id.
|
|
}
|
|
TDispatchMessage = record
|
|
MsgID: Word;
|
|
end;
|
|
|
|
TObject = class
|
|
constructor Create;
|
|
procedure Free;
|
|
class function InitInstance(Instance: Pointer): TObject;
|
|
procedure CleanupInstance;
|
|
function ClassType: TClass;
|
|
class function ClassName: ShortString;
|
|
class function ClassNameIs(const Name: string): Boolean;
|
|
class function ClassParent: TClass;
|
|
class function ClassInfo: Pointer;
|
|
class function InstanceSize: Longint;
|
|
class function InheritsFrom(AClass: TClass): Boolean;
|
|
class function MethodAddress(const Name: ShortString): Pointer;
|
|
class function MethodName(Address: Pointer): ShortString;
|
|
function FieldAddress(const Name: ShortString): Pointer;
|
|
function GetInterface(const IID: TGUID; out Obj): Boolean;
|
|
class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
|
|
class function GetInterfaceTable: PInterfaceTable;
|
|
function SafeCallException(ExceptObject: TObject;
|
|
ExceptAddr: Pointer): HResult; virtual;
|
|
procedure AfterConstruction; virtual;
|
|
procedure BeforeDestruction; virtual;
|
|
procedure Dispatch(var Message); virtual;
|
|
procedure DefaultHandler(var Message); virtual;
|
|
class function NewInstance: TObject; virtual;
|
|
procedure FreeInstance; virtual;
|
|
destructor Destroy; virtual;
|
|
end;
|
|
|
|
const
|
|
S_OK = 0; {$EXTERNALSYM S_OK}
|
|
S_FALSE = $00000001; {$EXTERNALSYM S_FALSE}
|
|
E_NOINTERFACE = HRESULT($80004002); {$EXTERNALSYM E_NOINTERFACE}
|
|
E_UNEXPECTED = HRESULT($8000FFFF); {$EXTERNALSYM E_UNEXPECTED}
|
|
E_NOTIMPL = HRESULT($80004001); {$EXTERNALSYM E_NOTIMPL}
|
|
|
|
type
|
|
IInterface = interface
|
|
['{00000000-0000-0000-C000-000000000046}']
|
|
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
|
|
function _AddRef: Integer; stdcall;
|
|
function _Release: Integer; stdcall;
|
|
end;
|
|
|
|
(*$HPPEMIT '#define IInterface IUnknown' *)
|
|
|
|
IUnknown = IInterface;
|
|
{$M+}
|
|
IInvokable = interface(IInterface)
|
|
end;
|
|
{$M-}
|
|
|
|
IDispatch = interface(IUnknown)
|
|
['{00020400-0000-0000-C000-000000000046}']
|
|
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
|
|
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
|
|
function GetIDsOfNames(const IID: TGUID; Names: Pointer;
|
|
NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
|
|
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
|
|
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
|
|
end;
|
|
|
|
{$EXTERNALSYM IUnknown}
|
|
{$EXTERNALSYM IDispatch}
|
|
|
|
{ TInterfacedObject provides a threadsafe default implementation
|
|
of IInterface. You should use TInterfaceObject as the base class
|
|
of objects implementing interfaces. }
|
|
|
|
TInterfacedObject = class(TObject, IInterface)
|
|
protected
|
|
FRefCount: Integer;
|
|
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
|
|
function _AddRef: Integer; stdcall;
|
|
function _Release: Integer; stdcall;
|
|
public
|
|
procedure AfterConstruction; override;
|
|
procedure BeforeDestruction; override;
|
|
class function NewInstance: TObject; override;
|
|
property RefCount: Integer read FRefCount;
|
|
end;
|
|
|
|
TInterfacedClass = class of TInterfacedObject;
|
|
|
|
{ TAggregatedObject and TContainedObject are suitable base
|
|
classes for interfaced objects intended to be aggregated
|
|
or contained in an outer controlling object. When using
|
|
the "implements" syntax on an interface property in
|
|
an outer object class declaration, use these types
|
|
to implement the inner object.
|
|
|
|
Interfaces implemented by aggregated objects on behalf of
|
|
the controller should not be distinguishable from other
|
|
interfaces provided by the controller. Aggregated objects
|
|
must not maintain their own reference count - they must
|
|
have the same lifetime as their controller. To achieve this,
|
|
aggregated objects reflect the reference count methods
|
|
to the controller.
|
|
|
|
TAggregatedObject simply reflects QueryInterface calls to
|
|
its controller. From such an aggregated object, one can
|
|
obtain any interface that the controller supports, and
|
|
only interfaces that the controller supports. This is
|
|
useful for implementing a controller class that uses one
|
|
or more internal objects to implement the interfaces declared
|
|
on the controller class. Aggregation promotes implementation
|
|
sharing across the object hierarchy.
|
|
|
|
TAggregatedObject is what most aggregate objects should
|
|
inherit from, especially when used in conjunction with
|
|
the "implements" syntax. }
|
|
|
|
TAggregatedObject = class(TObject)
|
|
private
|
|
FController: Pointer; // weak reference to controller
|
|
function GetController: IInterface;
|
|
protected
|
|
{ IInterface }
|
|
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
|
|
function _AddRef: Integer; stdcall;
|
|
function _Release: Integer; stdcall;
|
|
public
|
|
constructor Create(const Controller: IInterface);
|
|
property Controller: IInterface read GetController;
|
|
end;
|
|
|
|
{ TContainedObject is an aggregated object that isolates
|
|
QueryInterface on the aggregate from the controller.
|
|
TContainedObject will return only interfaces that the
|
|
contained object itself implements, not interfaces
|
|
that the controller implements. This is useful for
|
|
implementing nodes that are attached to a controller and
|
|
have the same lifetime as the controller, but whose
|
|
interface identity is separate from the controller.
|
|
You might do this if you don't want the consumers of
|
|
an aggregated interface to have access to other interfaces
|
|
implemented by the controller - forced encapsulation.
|
|
This is a less common case than TAggregatedObject. }
|
|
|
|
TContainedObject = class(TAggregatedObject, IInterface)
|
|
protected
|
|
{ IInterface }
|
|
function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
|
|
end;
|
|
|
|
PShortString = ^ShortString;
|
|
PAnsiString = ^AnsiString;
|
|
PWideString = ^WideString;
|
|
PString = PAnsiString;
|
|
|
|
UCS2Char = WideChar;
|
|
PUCS2Char = PWideChar;
|
|
UCS4Char = type LongWord;
|
|
{$NODEFINE UCS4CHAR}
|
|
PUCS4Char = ^UCS4Char;
|
|
{$NODEFINE PUCS4CHAR}
|
|
TUCS4CharArray = array [0..$effffff] of UCS4Char;
|
|
PUCS4CharArray = ^TUCS4CharArray;
|
|
UCS4String = array of UCS4Char;
|
|
{$NODEFINE UCS4String}
|
|
|
|
UTF8String = type string;
|
|
PUTF8String = ^UTF8String;
|
|
{$NODEFINE UTF8String}
|
|
{$NODEFINE PUTF8String}
|
|
|
|
IntegerArray = array[0..$effffff] of Integer;
|
|
PIntegerArray = ^IntegerArray;
|
|
PointerArray = array [0..512*1024*1024 - 2] of Pointer;
|
|
PPointerArray = ^PointerArray;
|
|
TBoundArray = array of Integer;
|
|
TPCharArray = packed array[0..(MaxLongint div SizeOf(PChar))-1] of PChar;
|
|
PPCharArray = ^TPCharArray;
|
|
|
|
(*$HPPEMIT 'namespace System' *)
|
|
(*$HPPEMIT '{' *)
|
|
(*$HPPEMIT ' typedef int *PLongint;' *)
|
|
(*$HPPEMIT ' typedef bool *PBoolean;' *)
|
|
(*$HPPEMIT ' typedef PChar *PPChar;' *)
|
|
(*$HPPEMIT ' typedef double *PDouble;' *)
|
|
(*$HPPEMIT ' typedef wchar_t UCS4Char;' *)
|
|
(*$HPPEMIT ' typedef wchar_t *PUCS4Char;' *)
|
|
(*$HPPEMIT ' typedef DynamicArray<UCS4Char> UCS4String;' *)
|
|
(*$HPPEMIT '}' *)
|
|
PLongint = ^Longint;
|
|
{$EXTERNALSYM PLongint}
|
|
PInteger = ^Integer;
|
|
PCardinal = ^Cardinal;
|
|
PWord = ^Word;
|
|
PSmallInt = ^SmallInt;
|
|
PByte = ^Byte;
|
|
PShortInt = ^ShortInt;
|
|
PInt64 = ^Int64;
|
|
PLongWord = ^LongWord;
|
|
PSingle = ^Single;
|
|
PDouble = ^Double;
|
|
PDate = ^Double;
|
|
PDispatch = ^IDispatch;
|
|
PPDispatch = ^PDispatch;
|
|
PError = ^LongWord;
|
|
PWordBool = ^WordBool;
|
|
PUnknown = ^IUnknown;
|
|
PPUnknown = ^PUnknown;
|
|
{$NODEFINE PByte}
|
|
PPWideChar = ^PWideChar;
|
|
PPChar = ^PChar;
|
|
PPAnsiChar = PPChar;
|
|
PExtended = ^Extended;
|
|
PComp = ^Comp;
|
|
PCurrency = ^Currency;
|
|
PVariant = ^Variant;
|
|
POleVariant = ^OleVariant;
|
|
PPointer = ^Pointer;
|
|
PBoolean = ^Boolean;
|
|
|
|
TDateTime = type Double;
|
|
PDateTime = ^TDateTime;
|
|
|
|
THandle = LongWord;
|
|
|
|
TVarArrayBound = packed record
|
|
ElementCount: Integer;
|
|
LowBound: Integer;
|
|
end;
|
|
TVarArrayBoundArray = array [0..0] of TVarArrayBound;
|
|
PVarArrayBoundArray = ^TVarArrayBoundArray;
|
|
TVarArrayCoorArray = array [0..0] of Integer;
|
|
PVarArrayCoorArray = ^TVarArrayCoorArray;
|
|
|
|
PVarArray = ^TVarArray;
|
|
TVarArray = packed record
|
|
DimCount: Word;
|
|
Flags: Word;
|
|
ElementSize: Integer;
|
|
LockCount: Integer;
|
|
Data: Pointer;
|
|
Bounds: TVarArrayBoundArray;
|
|
end;
|
|
|
|
TVarType = Word;
|
|
PVarData = ^TVarData;
|
|
{$EXTERNALSYM PVarData}
|
|
TVarData = packed record
|
|
VType: TVarType;
|
|
case Integer of
|
|
0: (Reserved1: Word;
|
|
case Integer of
|
|
0: (Reserved2, Reserved3: Word;
|
|
case Integer of
|
|
varSmallInt: (VSmallInt: SmallInt);
|
|
varInteger: (VInteger: Integer);
|
|
varSingle: (VSingle: Single);
|
|
varDouble: (VDouble: Double);
|
|
varCurrency: (VCurrency: Currency);
|
|
varDate: (VDate: TDateTime);
|
|
varOleStr: (VOleStr: PWideChar);
|
|
varDispatch: (VDispatch: Pointer);
|
|
varError: (VError: LongWord);
|
|
varBoolean: (VBoolean: WordBool);
|
|
varUnknown: (VUnknown: Pointer);
|
|
varShortInt: (VShortInt: ShortInt);
|
|
varByte: (VByte: Byte);
|
|
varWord: (VWord: Word);
|
|
varLongWord: (VLongWord: LongWord);
|
|
varInt64: (VInt64: Int64);
|
|
varString: (VString: Pointer);
|
|
varAny: (VAny: Pointer);
|
|
varArray: (VArray: PVarArray);
|
|
varByRef: (VPointer: Pointer);
|
|
);
|
|
1: (VLongs: array[0..2] of LongInt);
|
|
);
|
|
2: (VWords: array [0..6] of Word);
|
|
3: (VBytes: array [0..13] of Byte);
|
|
end;
|
|
{$EXTERNALSYM TVarData}
|
|
|
|
type
|
|
TVarOp = Integer;
|
|
|
|
const
|
|
opAdd = 0;
|
|
opSubtract = 1;
|
|
opMultiply = 2;
|
|
opDivide = 3;
|
|
opIntDivide = 4;
|
|
opModulus = 5;
|
|
opShiftLeft = 6;
|
|
opShiftRight = 7;
|
|
opAnd = 8;
|
|
opOr = 9;
|
|
opXor = 10;
|
|
opCompare = 11;
|
|
opNegate = 12;
|
|
opNot = 13;
|
|
|
|
opCmpEQ = 14;
|
|
opCmpNE = 15;
|
|
opCmpLT = 16;
|
|
opCmpLE = 17;
|
|
opCmpGT = 18;
|
|
opCmpGE = 19;
|
|
|
|
type
|
|
{ Dispatch call descriptor }
|
|
PCallDesc = ^TCallDesc;
|
|
TCallDesc = packed record
|
|
CallType: Byte;
|
|
ArgCount: Byte;
|
|
NamedArgCount: Byte;
|
|
ArgTypes: array[0..255] of Byte;
|
|
end;
|
|
|
|
PDispDesc = ^TDispDesc;
|
|
TDispDesc = packed record
|
|
DispID: Integer;
|
|
ResType: Byte;
|
|
CallDesc: TCallDesc;
|
|
end;
|
|
|
|
PVariantManager = ^TVariantManager;
|
|
{$EXTERNALSYM PVariantManager}
|
|
TVariantManager = record
|
|
VarClear: procedure(var V : Variant);
|
|
VarCopy: procedure(var Dest: Variant; const Source: Variant);
|
|
VarCopyNoInd: procedure; // ARGS PLEASE!
|
|
VarCast: procedure(var Dest: Variant; const Source: Variant; VarType: Integer);
|
|
VarCastOle: procedure(var Dest: Variant; const Source: Variant; VarType: Integer);
|
|
|
|
VarToInt: function(const V: Variant): Integer;
|
|
VarToInt64: function(const V: Variant): Int64;
|
|
VarToBool: function(const V: Variant): Boolean;
|
|
VarToReal: function(const V: Variant): Extended;
|
|
VarToCurr: function(const V: Variant): Currency;
|
|
VarToPStr: procedure(var S; const V: Variant);
|
|
VarToLStr: procedure(var S: string; const V: Variant);
|
|
VarToWStr: procedure(var S: WideString; const V: Variant);
|
|
VarToIntf: procedure(var Unknown: IInterface; const V: Variant);
|
|
VarToDisp: procedure(var Dispatch: IDispatch; const V: Variant);
|
|
VarToDynArray: procedure(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer);
|
|
|
|
VarFromInt: procedure(var V: Variant; const Value, Range: Integer);
|
|
VarFromInt64: procedure(var V: Variant; const Value: Int64);
|
|
VarFromBool: procedure(var V: Variant; const Value: Boolean);
|
|
VarFromReal: procedure; // var V: Variant; const Value: Real
|
|
VarFromTDateTime: procedure; // var V: Variant; const Value: TDateTime
|
|
VarFromCurr: procedure; // var V: Variant; const Value: Currency
|
|
VarFromPStr: procedure(var V: Variant; const Value: ShortString);
|
|
VarFromLStr: procedure(var V: Variant; const Value: string);
|
|
VarFromWStr: procedure(var V: Variant; const Value: WideString);
|
|
VarFromIntf: procedure(var V: Variant; const Value: IInterface);
|
|
VarFromDisp: procedure(var V: Variant; const Value: IDispatch);
|
|
VarFromDynArray: procedure(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer);
|
|
OleVarFromPStr: procedure(var V: OleVariant; const Value: ShortString);
|
|
OleVarFromLStr: procedure(var V: OleVariant; const Value: string);
|
|
OleVarFromVar: procedure(var V: OleVariant; const Value: Variant);
|
|
OleVarFromInt: procedure(var V: OleVariant; const Value, Range: Integer);
|
|
|
|
VarOp: procedure(var Left: Variant; const Right: Variant; OpCode: TVarOp);
|
|
VarCmp: procedure(const Left, Right: TVarData; const OpCode: TVarOp); { result is set in the flags }
|
|
VarNeg: procedure(var V: Variant);
|
|
VarNot: procedure(var V: Variant);
|
|
|
|
DispInvoke: procedure(Dest: PVarData; const Source: TVarData;
|
|
CallDesc: PCallDesc; Params: Pointer); cdecl;
|
|
VarAddRef: procedure(var V: Variant);
|
|
|
|
VarArrayRedim: procedure(var A : Variant; HighBound: Integer);
|
|
VarArrayGet: function(var A: Variant; IndexCount: Integer;
|
|
Indices: Integer): Variant; cdecl;
|
|
VarArrayPut: procedure(var A: Variant; const Value: Variant;
|
|
IndexCount: Integer; Indices: Integer); cdecl;
|
|
|
|
WriteVariant: function(var T: Text; const V: Variant; Width: Integer): Pointer;
|
|
Write0Variant: function(var T: Text; const V: Variant): Pointer;
|
|
end;
|
|
{$EXTERNALSYM TVariantManager}
|
|
|
|
{ Dynamic array support }
|
|
PDynArrayTypeInfo = ^TDynArrayTypeInfo;
|
|
{$EXTERNALSYM PDynArrayTypeInfo}
|
|
TDynArrayTypeInfo = packed record
|
|
kind: Byte;
|
|
name: string[0];
|
|
elSize: Longint;
|
|
elType: ^PDynArrayTypeInfo;
|
|
varType: Integer;
|
|
end;
|
|
{$EXTERNALSYM TDynArrayTypeInfo}
|
|
|
|
PVarRec = ^TVarRec;
|
|
TVarRec = record { do not pack this record; it is compiler-generated }
|
|
case Byte of
|
|
vtInteger: (VInteger: Integer; VType: Byte);
|
|
vtBoolean: (VBoolean: Boolean);
|
|
vtChar: (VChar: Char);
|
|
vtExtended: (VExtended: PExtended);
|
|
vtString: (VString: PShortString);
|
|
vtPointer: (VPointer: Pointer);
|
|
vtPChar: (VPChar: PChar);
|
|
vtObject: (VObject: TObject);
|
|
vtClass: (VClass: TClass);
|
|
vtWideChar: (VWideChar: WideChar);
|
|
vtPWideChar: (VPWideChar: PWideChar);
|
|
vtAnsiString: (VAnsiString: Pointer);
|
|
vtCurrency: (VCurrency: PCurrency);
|
|
vtVariant: (VVariant: PVariant);
|
|
vtInterface: (VInterface: Pointer);
|
|
vtWideString: (VWideString: Pointer);
|
|
vtInt64: (VInt64: PInt64);
|
|
end;
|
|
|
|
PMemoryManager = ^TMemoryManager;
|
|
TMemoryManager = record
|
|
GetMem: function(Size: Integer): Pointer;
|
|
FreeMem: function(P: Pointer): Integer;
|
|
ReallocMem: function(P: Pointer; Size: Integer): Pointer;
|
|
end;
|
|
|
|
THeapStatus = record
|
|
TotalAddrSpace: Cardinal;
|
|
TotalUncommitted: Cardinal;
|
|
TotalCommitted: Cardinal;
|
|
TotalAllocated: Cardinal;
|
|
TotalFree: Cardinal;
|
|
FreeSmall: Cardinal;
|
|
FreeBig: Cardinal;
|
|
Unused: Cardinal;
|
|
Overhead: Cardinal;
|
|
HeapErrorCode: Cardinal;
|
|
end;
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
PUnwinder = ^TUnwinder;
|
|
TUnwinder = record
|
|
RaiseException: function(Exc: Pointer): LongBool; cdecl;
|
|
RegisterIPLookup: function(fn: Pointer; StartAddr, EndAddr: LongInt; Context: Pointer; GOT: LongInt): LongBool; cdecl;
|
|
UnregisterIPLookup: procedure(StartAddr: LongInt) cdecl;
|
|
DelphiLookup: function(Addr: LongInt; Context: Pointer): Pointer; cdecl;
|
|
ClosestHandler: function(Context: Pointer): LongWord; cdecl;
|
|
end;
|
|
{$ENDIF PC_MAPPED_EXCEPTIONS}
|
|
|
|
PackageUnitEntry = packed record
|
|
Init, FInit : Pointer;
|
|
end;
|
|
|
|
{ Compiler generated table to be processed sequentially to init & finit all package units }
|
|
{ Init: 0..Max-1; Final: Last Initialized..0 }
|
|
UnitEntryTable = array [0..9999999] of PackageUnitEntry;
|
|
PUnitEntryTable = ^UnitEntryTable;
|
|
|
|
PackageInfoTable = packed record
|
|
UnitCount : Integer; { number of entries in UnitInfo array; always > 0 }
|
|
UnitInfo : PUnitEntryTable;
|
|
end;
|
|
|
|
PackageInfo = ^PackageInfoTable;
|
|
|
|
{ Each package exports a '@GetPackageInfoTable' which can be used to retrieve }
|
|
{ the table which contains compiler generated information about the package DLL }
|
|
GetPackageInfoTable = function : PackageInfo;
|
|
|
|
{$IFDEF DEBUG_FUNCTIONS}
|
|
{ Inspector Query; implementation in GETMEM.INC; no need to conditionalize that }
|
|
THeapBlock = record
|
|
Start: Pointer;
|
|
Size: Cardinal;
|
|
end;
|
|
|
|
THeapBlockArray = array of THeapBlock;
|
|
TObjectArray = array of TObject;
|
|
|
|
function GetHeapBlocks: THeapBlockArray;
|
|
function FindObjects(AClass: TClass; FindDerived: Boolean): TObjectArray;
|
|
{ Inspector Query }
|
|
{$ENDIF}
|
|
|
|
{
|
|
When an exception is thrown, the exception object that is thrown is destroyed
|
|
automatically when the except clause which handles the exception is exited.
|
|
There are some cases in which an application may wish to acquire the thrown
|
|
object and keep it alive after the except clause is exited. For this purpose,
|
|
we have added the AcquireExceptionObject and ReleaseExceptionObject functions.
|
|
These functions maintain a reference count on the most current exception object,
|
|
allowing applications to legitimately obtain references. If the reference count
|
|
for an exception that is being thrown is positive when the except clause is exited,
|
|
then the thrown object is not destroyed by the RTL, but assumed to be in control
|
|
of the application. It is then the application's responsibility to destroy the
|
|
thrown object. If the reference count is zero, then the RTL will destroy the
|
|
thrown object when the except clause is exited.
|
|
}
|
|
function AcquireExceptionObject: Pointer;
|
|
procedure ReleaseExceptionObject;
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure GetUnwinder(var Dest: TUnwinder);
|
|
procedure SetUnwinder(const NewUnwinder: TUnwinder);
|
|
function IsUnwinderSet: Boolean;
|
|
|
|
//function SysRegisterIPLookup(ModuleHandle, StartAddr, EndAddr: LongInt; Context: Pointer; GOT: LongInt): LongBool;
|
|
{
|
|
Do NOT call these functions. They are for internal use only:
|
|
SysRegisterIPLookup
|
|
SysUnregisterIPLookup
|
|
BlockOSExceptions
|
|
UnblockOSExceptions
|
|
AreOSExceptionsBlocked
|
|
}
|
|
function SysRegisterIPLookup(StartAddr, EndAddr: LongInt; Context: Pointer; GOT: LongInt): LongBool;
|
|
procedure SysUnregisterIPLookup(StartAddr: LongInt);
|
|
//function SysAddressIsInPCMap(Addr: LongInt): Boolean;
|
|
function SysClosestDelphiHandler(Context: Pointer): LongWord;
|
|
procedure BlockOSExceptions;
|
|
procedure UnblockOSExceptions;
|
|
function AreOSExceptionsBlocked: Boolean;
|
|
|
|
{$ELSE}
|
|
// These functions are not portable. Use AcquireExceptionObject above instead
|
|
function RaiseList: Pointer; deprecated; { Stack of current exception objects }
|
|
function SetRaiseList(NewPtr: Pointer): Pointer; deprecated; { returns previous value }
|
|
{$ENDIF}
|
|
|
|
function ExceptObject: TObject;
|
|
function ExceptAddr: Pointer;
|
|
|
|
|
|
procedure SetInOutRes(NewValue: Integer);
|
|
|
|
type
|
|
TAssertErrorProc = procedure (const Message, Filename: string;
|
|
LineNumber: Integer; ErrorAddr: Pointer);
|
|
TSafeCallErrorProc = procedure (ErrorCode: HResult; ErrorAddr: Pointer);
|
|
|
|
{$IFDEF DEBUG}
|
|
{
|
|
This variable is just for debugging the exception handling system. See
|
|
_DbgExcNotify for the usage.
|
|
}
|
|
var
|
|
ExcNotificationProc : procedure ( NotificationKind: Integer;
|
|
ExceptionObject: Pointer;
|
|
ExceptionName: PShortString;
|
|
ExceptionLocation: Pointer;
|
|
HandlerAddr: Pointer) = nil;
|
|
{$ENDIF}
|
|
|
|
var
|
|
DispCallByIDProc: Pointer;
|
|
ExceptProc: Pointer; { Unhandled exception handler }
|
|
ErrorProc: procedure (ErrorCode: Byte; ErrorAddr: Pointer); { Error handler procedure }
|
|
{$IFDEF MSWINDOWS}
|
|
ExceptClsProc: Pointer; { Map an OS Exception to a Delphi class reference }
|
|
ExceptObjProc: Pointer; { Map an OS Exception to a Delphi class instance }
|
|
RaiseExceptionProc: Pointer;
|
|
RTLUnwindProc: Pointer;
|
|
{$ENDIF}
|
|
ExceptionClass: TClass; { Exception base class (must be Exception) }
|
|
SafeCallErrorProc: TSafeCallErrorProc; { Safecall error handler }
|
|
AssertErrorProc: TAssertErrorProc; { Assertion error handler }
|
|
ExitProcessProc: procedure; { Hook to be called just before the process actually exits }
|
|
AbstractErrorProc: procedure; { Abstract method error handler }
|
|
HPrevInst: LongWord deprecated; { Handle of previous instance - HPrevInst cannot be tested for multiple instances in Win32}
|
|
MainInstance: LongWord; { Handle of the main(.EXE) HInstance }
|
|
MainThreadID: LongWord; { ThreadID of thread that module was initialized in }
|
|
IsLibrary: Boolean; { True if module is a DLL }
|
|
{$IFDEF MSWINDOWS}
|
|
{X} // following variables are converted to functions
|
|
{X} function CmdShow : Integer;
|
|
{X} function CmdLine : PChar;
|
|
{$ELSE}
|
|
CmdShow: Integer platform; { CmdShow parameter for CreateWindow }
|
|
CmdLine: PChar platform; { Command line pointer }
|
|
{$ENDIF}
|
|
var
|
|
InitProc: Pointer; { Last installed initialization procedure }
|
|
ExitCode: Integer = 0; { Program result }
|
|
ExitProc: Pointer; { Last installed exit procedure }
|
|
ErrorAddr: Pointer = nil; { Address of run-time error }
|
|
RandSeed: Longint = 0; { Base for random number generator }
|
|
IsConsole: Boolean; { True if compiled as console app }
|
|
IsMultiThread: Boolean; { True if more than one thread }
|
|
FileMode: Byte = 2; { Standard mode for opening files }
|
|
{$IFDEF LINUX}
|
|
FileAccessRights: Integer platform; { Default access rights for opening files }
|
|
ArgCount: Integer platform;
|
|
ArgValues: PPChar platform;
|
|
{$ENDIF}
|
|
Test8086: Byte; { CPU family (minus one) See consts below }
|
|
Test8087: Byte = 3; { assume 80387 FPU or OS supplied FPU emulation }
|
|
TestFDIV: Shortint; { -1: Flawed Pentium, 0: Not determined, 1: Ok }
|
|
Input: Text; { Standard input }
|
|
Output: Text; { Standard output }
|
|
ErrOutput: Text; { Standard error output }
|
|
envp: PPChar platform;
|
|
|
|
const
|
|
CPUi386 = 2;
|
|
CPUi486 = 3;
|
|
CPUPentium = 4;
|
|
|
|
var
|
|
Default8087CW: Word = $1332;{ Default 8087 control word. FPU control
|
|
register is set to this value.
|
|
CAUTION: Setting this to an invalid value
|
|
could cause unpredictable behavior. }
|
|
|
|
HeapAllocFlags: Word platform = 2; { Heap allocation flags, gmem_Moveable }
|
|
DebugHook: Byte platform = 0; { 1 to notify debugger of non-Delphi exceptions
|
|
>1 to notify debugger of exception unwinding }
|
|
JITEnable: Byte platform = 0; { 1 to call UnhandledExceptionFilter if the exception
|
|
is not a Pascal exception.
|
|
>1 to call UnhandledExceptionFilter for all exceptions }
|
|
NoErrMsg: Boolean platform = False; { True causes the base RTL to not display the message box
|
|
when a run-time error occurs }
|
|
{$IFDEF LINUX}
|
|
{ CoreDumpEnabled = True will cause unhandled
|
|
exceptions and runtime errors to raise a
|
|
SIGABRT signal, which will cause the OS to
|
|
coredump the process address space. This can
|
|
be useful for postmortem debugging. }
|
|
CoreDumpEnabled: Boolean platform = False;
|
|
{$ENDIF}
|
|
|
|
type
|
|
(*$NODEFINE TTextLineBreakStyle*)
|
|
TTextLineBreakStyle = (tlbsLF, tlbsCRLF);
|
|
|
|
var { Text output line break handling. Default value for all text files }
|
|
DefaultTextLineBreakStyle: TTextLineBreakStyle = {$IFDEF LINUX} tlbsLF {$ENDIF}
|
|
{$IFDEF MSWINDOWS} tlbsCRLF {$ENDIF};
|
|
const
|
|
sLineBreak = {$IFDEF LINUX} #10 {$ENDIF} {$IFDEF MSWINDOWS} #13#10 {$ENDIF};
|
|
|
|
type
|
|
HRSRC = THandle;
|
|
TResourceHandle = HRSRC; // make an opaque handle type
|
|
HINST = THandle;
|
|
HMODULE = HINST;
|
|
HGLOBAL = THandle;
|
|
|
|
{$IFDEF ELF}
|
|
{ ELF resources }
|
|
function FindResource(ModuleHandle: HMODULE; ResourceName, ResourceType: PChar): TResourceHandle;
|
|
function LoadResource(ModuleHandle: HMODULE; ResHandle: TResourceHandle): HGLOBAL;
|
|
function SizeofResource(ModuleHandle: HMODULE; ResHandle: TResourceHandle): Integer;
|
|
function LockResource(ResData: HGLOBAL): Pointer;
|
|
function UnlockResource(ResData: HGLOBAL): LongBool;
|
|
function FreeResource(ResData: HGLOBAL): LongBool;
|
|
{$ENDIF}
|
|
|
|
{ Memory manager support }
|
|
|
|
{X} // By default, now system memory management routines are used
|
|
{X} // to allocate memory. This can be slow sometimes, so if You
|
|
{X} // want to use custom Borland Delphi memory manager, call follow:
|
|
{X} procedure UseDelphiMemoryManager;
|
|
{X} function IsDelphiMemoryManagerSet : Boolean;
|
|
{X} function MemoryManagerNotUsed : Boolean;
|
|
|
|
procedure GetMemoryManager(var MemMgr: TMemoryManager);
|
|
procedure SetMemoryManager(const MemMgr: TMemoryManager);
|
|
{X} // following function is replaced with pointer to one
|
|
{X} // (initialized by another)
|
|
{X} //function IsMemoryManagerSet: Boolean;
|
|
var IsMemoryManagerSet : function : Boolean = MemoryManagerNotUsed;
|
|
|
|
|
|
function SysGetMem(Size: Integer): Pointer;
|
|
function SysFreeMem(P: Pointer): Integer;
|
|
function SysReallocMem(P: Pointer; Size: Integer): Pointer;
|
|
|
|
var
|
|
AllocMemCount: Integer; { Number of allocated memory blocks }
|
|
AllocMemSize: Integer; { Total size of allocated memory blocks }
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
function GetHeapStatus: THeapStatus; platform;
|
|
{$ENDIF}
|
|
|
|
{ Thread support }
|
|
type
|
|
TThreadFunc = function(Parameter: Pointer): Integer;
|
|
{$IFDEF LINUX}
|
|
TSize_T = Cardinal;
|
|
|
|
TSchedParam = record
|
|
sched_priority: Integer;
|
|
end;
|
|
|
|
pthread_attr_t = record
|
|
__detachstate,
|
|
__schedpolicy: Integer;
|
|
__schedparam: TSchedParam;
|
|
__inheritsched,
|
|
__scope: Integer;
|
|
__guardsize: TSize_T;
|
|
__stackaddr_set: Integer;
|
|
__stackaddr: Pointer;
|
|
__stacksize: TSize_T;
|
|
end;
|
|
{$EXTERNALSYM pthread_attr_t}
|
|
TThreadAttr = pthread_attr_t;
|
|
PThreadAttr = ^TThreadAttr;
|
|
|
|
TBeginThreadProc = function (Attribute: PThreadAttr;
|
|
ThreadFunc: TThreadFunc; Parameter: Pointer;
|
|
var ThreadId: Cardinal): Integer;
|
|
TEndThreadProc = procedure(ExitCode: Integer);
|
|
|
|
var
|
|
BeginThreadProc: TBeginThreadProc = nil;
|
|
EndThreadProc: TEndThreadProc = nil;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
|
|
ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
|
|
var ThreadId: LongWord): Integer;
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
function BeginThread(Attribute: PThreadAttr; ThreadFunc: TThreadFunc;
|
|
Parameter: Pointer; var ThreadId: Cardinal): Integer;
|
|
|
|
{$ENDIF}
|
|
procedure EndThread(ExitCode: Integer);
|
|
|
|
{ Standard procedures and functions }
|
|
|
|
const
|
|
{ File mode magic numbers }
|
|
|
|
fmClosed = $D7B0;
|
|
fmInput = $D7B1;
|
|
fmOutput = $D7B2;
|
|
fmInOut = $D7B3;
|
|
|
|
{ Text file flags }
|
|
tfCRLF = $1; // Dos compatibility flag, for CR+LF line breaks and EOF checks
|
|
|
|
type
|
|
{ Typed-file and untyped-file record }
|
|
|
|
TFileRec = packed record (* must match the size the compiler generates: 332 bytes *)
|
|
Handle: Integer;
|
|
Mode: Word;
|
|
Flags: Word;
|
|
case Byte of
|
|
0: (RecSize: Cardinal); // files of record
|
|
1: (BufSize: Cardinal; // text files
|
|
BufPos: Cardinal;
|
|
BufEnd: Cardinal;
|
|
BufPtr: PChar;
|
|
OpenFunc: Pointer;
|
|
InOutFunc: Pointer;
|
|
FlushFunc: Pointer;
|
|
CloseFunc: Pointer;
|
|
UserData: array[1..32] of Byte;
|
|
Name: array[0..259] of Char; );
|
|
end;
|
|
|
|
{ Text file record structure used for Text files }
|
|
PTextBuf = ^TTextBuf;
|
|
TTextBuf = array[0..127] of Char;
|
|
TTextRec = packed record (* must match the size the compiler generates: 460 bytes *)
|
|
Handle: Integer; (* must overlay with TFileRec *)
|
|
Mode: Word;
|
|
Flags: Word;
|
|
BufSize: Cardinal;
|
|
BufPos: Cardinal;
|
|
BufEnd: Cardinal;
|
|
BufPtr: PChar;
|
|
OpenFunc: Pointer;
|
|
InOutFunc: Pointer;
|
|
FlushFunc: Pointer;
|
|
CloseFunc: Pointer;
|
|
UserData: array[1..32] of Byte;
|
|
Name: array[0..259] of Char;
|
|
Buffer: TTextBuf;
|
|
end;
|
|
|
|
TTextIOFunc = function (var F: TTextRec): Integer;
|
|
TFileIOFunc = function (var F: TFileRec): Integer;
|
|
|
|
procedure SetLineBreakStyle(var T: Text; Style: TTextLineBreakStyle);
|
|
|
|
procedure ChDir(const S: string); overload;
|
|
procedure ChDir(P: PChar); overload;
|
|
function Flush(var t: Text): Integer;
|
|
procedure _LGetDir(D: Byte; var S: string);
|
|
procedure _SGetDir(D: Byte; var S: ShortString);
|
|
function IOResult: Integer;
|
|
procedure MkDir(const S: string); overload;
|
|
procedure MkDir(P: PChar); overload;
|
|
procedure Move(const Source; var Dest; Count: Integer);
|
|
function ParamCount: Integer;
|
|
function ParamStr(Index: Integer): string;
|
|
procedure RmDir(const S: string); overload;
|
|
procedure RmDir(P: PChar); overload;
|
|
function UpCase(Ch: Char): Char;
|
|
|
|
{ random functions }
|
|
procedure Randomize;
|
|
|
|
function Random(const ARange: Integer): Integer; overload;
|
|
function Random: Extended; overload;
|
|
|
|
{ Control 8087 control word }
|
|
|
|
procedure Set8087CW(NewCW: Word);
|
|
function Get8087CW: Word;
|
|
|
|
{ Wide character support procedures and functions for C++ }
|
|
{ These functions should not be used in Delphi code!
|
|
(conversion is implicit in Delphi code) }
|
|
|
|
function WideCharToString(Source: PWideChar): string;
|
|
function WideCharLenToString(Source: PWideChar; SourceLen: Integer): string;
|
|
procedure WideCharToStrVar(Source: PWideChar; var Dest: string);
|
|
procedure WideCharLenToStrVar(Source: PWideChar; SourceLen: Integer;
|
|
var Dest: string);
|
|
function StringToWideChar(const Source: string; Dest: PWideChar;
|
|
DestSize: Integer): PWideChar;
|
|
|
|
{ PUCS4Chars returns a pointer to the UCS4 char data in the
|
|
UCS4String array, or a pointer to a null char if UCS4String is empty }
|
|
|
|
function PUCS4Chars(const S: UCS4String): PUCS4Char;
|
|
|
|
{ Widestring <-> UCS4 conversion }
|
|
|
|
function WideStringToUCS4String(const S: WideString): UCS4String;
|
|
function UCS4StringToWideString(const S: UCS4String): WideString;
|
|
|
|
{ PChar/PWideChar Unicode <-> UTF8 conversion }
|
|
|
|
// UnicodeToUTF8(3):
|
|
// UTF8ToUnicode(3):
|
|
// Scans the source data to find the null terminator, up to MaxBytes
|
|
// Dest must have MaxBytes available in Dest.
|
|
// MaxDestBytes includes the null terminator (last char in the buffer will be set to null)
|
|
// Function result includes the null terminator.
|
|
|
|
function UnicodeToUtf8(Dest: PChar; Source: PWideChar; MaxBytes: Integer): Integer; overload; deprecated;
|
|
function Utf8ToUnicode(Dest: PWideChar; Source: PChar; MaxChars: Integer): Integer; overload; deprecated;
|
|
|
|
// UnicodeToUtf8(4):
|
|
// UTF8ToUnicode(4):
|
|
// MaxDestBytes includes the null terminator (last char in the buffer will be set to null)
|
|
// Function result includes the null terminator.
|
|
// Nulls in the source data are not considered terminators - SourceChars must be accurate
|
|
|
|
function UnicodeToUtf8(Dest: PChar; MaxDestBytes: Cardinal; Source: PWideChar; SourceChars: Cardinal): Cardinal; overload;
|
|
function Utf8ToUnicode(Dest: PWideChar; MaxDestChars: Cardinal; Source: PChar; SourceBytes: Cardinal): Cardinal; overload;
|
|
|
|
{ WideString <-> UTF8 conversion }
|
|
|
|
function UTF8Encode(const WS: WideString): UTF8String;
|
|
function UTF8Decode(const S: UTF8String): WideString;
|
|
|
|
{ Ansi <-> UTF8 conversion }
|
|
|
|
function AnsiToUtf8(const S: string): UTF8String;
|
|
function Utf8ToAnsi(const S: UTF8String): string;
|
|
|
|
{ OLE string support procedures and functions }
|
|
|
|
function OleStrToString(Source: PWideChar): string;
|
|
procedure OleStrToStrVar(Source: PWideChar; var Dest: string);
|
|
function StringToOleStr(const Source: string): PWideChar;
|
|
|
|
{ Variant manager support procedures and functions }
|
|
|
|
procedure GetVariantManager(var VarMgr: TVariantManager);
|
|
procedure SetVariantManager(const VarMgr: TVariantManager);
|
|
function IsVariantManagerSet: Boolean;
|
|
|
|
{ Variant support procedures and functions }
|
|
|
|
procedure _VarClear(var V: Variant);
|
|
procedure _VarCopy(var Dest: Variant; const Source: Variant);
|
|
procedure _VarCopyNoInd;
|
|
procedure _VarCast(var Dest: Variant; const Source: Variant; VarType: Integer);
|
|
procedure _VarCastOle(var Dest: Variant; const Source: Variant; VarType: Integer);
|
|
procedure _VarClr(var V: Variant);
|
|
|
|
{ Variant text streaming support }
|
|
|
|
function _WriteVariant(var T: Text; const V: Variant; Width: Integer): Pointer;
|
|
function _Write0Variant(var T: Text; const V: Variant): Pointer;
|
|
|
|
{ Variant math and conversion support }
|
|
|
|
function _VarToInt(const V: Variant): Integer;
|
|
function _VarToInt64(const V: Variant): Int64;
|
|
function _VarToBool(const V: Variant): Boolean;
|
|
function _VarToReal(const V: Variant): Extended;
|
|
function _VarToCurr(const V: Variant): Currency;
|
|
procedure _VarToPStr(var S; const V: Variant);
|
|
procedure _VarToLStr(var S: string; const V: Variant);
|
|
procedure _VarToWStr(var S: WideString; const V: Variant);
|
|
procedure _VarToIntf(var Unknown: IInterface; const V: Variant);
|
|
procedure _VarToDisp(var Dispatch: IDispatch; const V: Variant);
|
|
procedure _VarToDynArray(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer);
|
|
|
|
procedure _VarFromInt(var V: Variant; const Value, Range: Integer);
|
|
procedure _VarFromInt64(var V: Variant; const Value: Int64);
|
|
procedure _VarFromBool(var V: Variant; const Value: Boolean);
|
|
procedure _VarFromReal; // var V: Variant; const Value: Real
|
|
procedure _VarFromTDateTime; // var V: Variant; const Value: TDateTime
|
|
procedure _VarFromCurr; // var V: Variant; const Value: Currency
|
|
procedure _VarFromPStr(var V: Variant; const Value: ShortString);
|
|
procedure _VarFromLStr(var V: Variant; const Value: string);
|
|
procedure _VarFromWStr(var V: Variant; const Value: WideString);
|
|
procedure _VarFromIntf(var V: Variant; const Value: IInterface);
|
|
procedure _VarFromDisp(var V: Variant; const Value: IDispatch);
|
|
procedure _VarFromDynArray(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer);
|
|
procedure _OleVarFromPStr(var V: OleVariant; const Value: ShortString);
|
|
procedure _OleVarFromLStr(var V: OleVariant; const Value: string);
|
|
procedure _OleVarFromVar(var V: OleVariant; const Value: Variant);
|
|
procedure _OleVarFromInt(var V: OleVariant; const Value, Range: Integer);
|
|
|
|
procedure _VarAdd(var Left: Variant; const Right: Variant);
|
|
procedure _VarSub(var Left: Variant; const Right: Variant);
|
|
procedure _VarMul(var Left: Variant; const Right: Variant);
|
|
procedure _VarDiv(var Left: Variant; const Right: Variant);
|
|
procedure _VarMod(var Left: Variant; const Right: Variant);
|
|
procedure _VarAnd(var Left: Variant; const Right: Variant);
|
|
procedure _VarOr(var Left: Variant; const Right: Variant);
|
|
procedure _VarXor(var Left: Variant; const Right: Variant);
|
|
procedure _VarShl(var Left: Variant; const Right: Variant);
|
|
procedure _VarShr(var Left: Variant; const Right: Variant);
|
|
procedure _VarRDiv(var Left: Variant; const Right: Variant);
|
|
|
|
procedure _VarCmpEQ(const Left, Right: Variant); // result is set in the flags
|
|
procedure _VarCmpNE(const Left, Right: Variant); // result is set in the flags
|
|
procedure _VarCmpLT(const Left, Right: Variant); // result is set in the flags
|
|
procedure _VarCmpLE(const Left, Right: Variant); // result is set in the flags
|
|
procedure _VarCmpGT(const Left, Right: Variant); // result is set in the flags
|
|
procedure _VarCmpGE(const Left, Right: Variant); // result is set in the flags
|
|
|
|
procedure _VarNeg(var V: Variant);
|
|
procedure _VarNot(var V: Variant);
|
|
|
|
{ Variant dispatch and reference support }
|
|
|
|
procedure _DispInvoke; cdecl; // Dest: PVarData; const Source: TVarData;
|
|
// CallDesc: PCallDesc; Params: Pointer
|
|
procedure _IntfDispCall; cdecl; // ARGS PLEASE!
|
|
procedure _IntfVarCall; cdecl; // ARGS PLEASE!
|
|
procedure _VarAddRef(var V: Variant);
|
|
|
|
{ Variant array support procedures and functions }
|
|
|
|
procedure _VarArrayRedim(var A : Variant; HighBound: Integer);
|
|
function _VarArrayGet(var A: Variant; IndexCount: Integer;
|
|
Indices: Integer): Variant; cdecl;
|
|
procedure _VarArrayPut(var A: Variant; const Value: Variant;
|
|
IndexCount: Integer; Indices: Integer); cdecl;
|
|
|
|
{ Package/Module registration and unregistration }
|
|
|
|
type
|
|
PLibModule = ^TLibModule;
|
|
TLibModule = record
|
|
Next: PLibModule;
|
|
Instance: LongWord;
|
|
CodeInstance: LongWord;
|
|
DataInstance: LongWord;
|
|
ResInstance: LongWord;
|
|
Reserved: Integer;
|
|
{$IFDEF LINUX}
|
|
InstanceVar: Pointer platform;
|
|
GOT: LongWord platform;
|
|
CodeSegStart: LongWord platform;
|
|
CodeSegEnd: LongWord platform;
|
|
InitTable: Pointer platform;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
TEnumModuleFunc = function (HInstance: Integer; Data: Pointer): Boolean;
|
|
{$EXTERNALSYM TEnumModuleFunc}
|
|
TEnumModuleFuncLW = function (HInstance: LongWord; Data: Pointer): Boolean;
|
|
{$EXTERNALSYM TEnumModuleFuncLW}
|
|
TModuleUnloadProc = procedure (HInstance: Integer);
|
|
{$EXTERNALSYM TModuleUnloadProc}
|
|
TModuleUnloadProcLW = procedure (HInstance: LongWord);
|
|
{$EXTERNALSYM TModuleUnloadProcLW}
|
|
|
|
PModuleUnloadRec = ^TModuleUnloadRec;
|
|
TModuleUnloadRec = record
|
|
Next: PModuleUnloadRec;
|
|
Proc: TModuleUnloadProcLW;
|
|
end;
|
|
|
|
var
|
|
LibModuleList: PLibModule = nil;
|
|
ModuleUnloadList: PModuleUnloadRec = nil;
|
|
|
|
procedure RegisterModule(LibModule: PLibModule);
|
|
{X procedure UnregisterModule(LibModule: PLibModule); -replaced with pointer to procedure }
|
|
{X} procedure UnregisterModuleLight(LibModule: PLibModule);
|
|
{X} procedure UnregisterModuleSafely(LibModule: PLibModule);
|
|
var UnregisterModule : procedure(LibModule: PLibModule) = UnregisterModuleLight;
|
|
function FindHInstance(Address: Pointer): LongWord;
|
|
function FindClassHInstance(ClassType: TClass): LongWord;
|
|
function FindResourceHInstance(Instance: LongWord): LongWord;
|
|
function LoadResourceModule(ModuleName: PChar; CheckOwner: Boolean = True): LongWord;
|
|
procedure EnumModules(Func: TEnumModuleFunc; Data: Pointer); overload;
|
|
procedure EnumResourceModules(Func: TEnumModuleFunc; Data: Pointer); overload;
|
|
procedure EnumModules(Func: TEnumModuleFuncLW; Data: Pointer); overload;
|
|
procedure EnumResourceModules(Func: TEnumModuleFuncLW; Data: Pointer); overload;
|
|
procedure AddModuleUnloadProc(Proc: TModuleUnloadProc); overload;
|
|
procedure RemoveModuleUnloadProc(Proc: TModuleUnloadProc); overload;
|
|
procedure AddModuleUnloadProc(Proc: TModuleUnloadProcLW); overload;
|
|
procedure RemoveModuleUnloadProc(Proc: TModuleUnloadProcLW); overload;
|
|
{$IFDEF LINUX}
|
|
{ Given an HMODULE, this function will return its fully qualified name. There is
|
|
no direct equivalent in Linux so this function provides that capability. }
|
|
function GetModuleFileName(Module: HMODULE; Buffer: PChar; BufLen: Integer): Integer;
|
|
{$ENDIF}
|
|
|
|
{ ResString support function/record }
|
|
|
|
type
|
|
PResStringRec = ^TResStringRec;
|
|
TResStringRec = packed record
|
|
Module: ^Cardinal;
|
|
Identifier: Integer;
|
|
end;
|
|
|
|
function LoadResString(ResStringRec: PResStringRec): string;
|
|
|
|
{ Procedures and functions that need compiler magic }
|
|
|
|
function Int(const X: Extended): Extended;
|
|
function Frac(const X: Extended): Extended;
|
|
function Exp(const X: Extended): Extended;
|
|
function Cos(const X: Extended): Extended;
|
|
function Sin(const X: Extended): Extended;
|
|
function Ln(const X: Extended): Extended;
|
|
function ArcTan(const X: Extended): Extended;
|
|
function Sqrt(const X: Extended): Extended;
|
|
|
|
procedure _ROUND;
|
|
procedure _TRUNC;
|
|
|
|
procedure _AbstractError;
|
|
procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);
|
|
function _Append(var t: TTextRec): Integer;
|
|
function _Assign(var t: TTextRec; const S: String): Integer;
|
|
function _BlockRead(var f: TFileRec; buffer: Pointer; recCnt: Longint; var recsRead: Longint): Longint;
|
|
function _BlockWrite(var f: TFileRec; buffer: Pointer; recCnt: Longint; var recsWritten: Longint): Longint;
|
|
function _Close(var t: TTextRec): Integer;
|
|
procedure _PStrCat;
|
|
procedure _PStrNCat;
|
|
procedure _PStrCpy(Dest: PShortString; Source: PShortString);
|
|
procedure _PStrNCpy(Dest: PShortString; Source: PShortString; MaxLen: Byte);
|
|
function _EofFile(var f: TFileRec): Boolean;
|
|
function _EofText(var t: TTextRec): Boolean;
|
|
function _Eoln(var t: TTextRec): Boolean;
|
|
procedure _Erase(var f: TFileRec);
|
|
function _FilePos(var f: TFileRec): Longint;
|
|
function _FileSize(var f: TFileRec): Longint;
|
|
procedure _FillChar(var Dest; count: Integer; Value: Char);
|
|
function _FreeMem(P: Pointer): Integer;
|
|
function _GetMem(Size: Integer): Pointer;
|
|
function _ReallocMem(var P: Pointer; NewSize: Integer): Pointer;
|
|
procedure _Halt(Code: Integer);
|
|
procedure _Halt0;
|
|
procedure Mark; deprecated;
|
|
procedure _PStrCmp;
|
|
procedure _AStrCmp;
|
|
procedure _RandInt;
|
|
procedure _RandExt;
|
|
function _ReadRec(var f: TFileRec; Buffer: Pointer): Integer;
|
|
function _ReadChar(var t: TTextRec): Char;
|
|
function _ReadLong(var t: TTextRec): Longint;
|
|
procedure _ReadString(var t: TTextRec; s: PShortString; maxLen: Longint);
|
|
procedure _ReadCString(var t: TTextRec; s: PChar; maxLen: Longint);
|
|
procedure _ReadLString(var t: TTextRec; var s: AnsiString);
|
|
procedure _ReadWString(var t: TTextRec; var s: WideString);
|
|
procedure _ReadWCString(var t: TTextRec; s: PWideChar; maxBytes: Longint);
|
|
function _ReadWChar(var t: TTextRec): WideChar;
|
|
function _ReadExt(var t: TTextRec): Extended;
|
|
procedure _ReadLn(var t: TTextRec);
|
|
procedure _Rename(var f: TFileRec; newName: PChar);
|
|
procedure Release; deprecated;
|
|
function _ResetText(var t: TTextRec): Integer;
|
|
function _ResetFile(var f: TFileRec; recSize: Longint): Integer;
|
|
function _RewritText(var t: TTextRec): Integer;
|
|
function _RewritFile(var f: TFileRec; recSize: Longint): Integer;
|
|
procedure _RunError(errorCode: Byte);
|
|
procedure _Run0Error;
|
|
procedure _Seek(var f: TFileRec; recNum: Cardinal);
|
|
function _SeekEof(var t: TTextRec): Boolean;
|
|
function _SeekEoln(var t: TTextRec): Boolean;
|
|
procedure _SetTextBuf(var t: TTextRec; p: Pointer; size: Longint);
|
|
procedure _StrLong(val, width: Longint; s: PShortString);
|
|
procedure _Str0Long(val: Longint; s: PShortString);
|
|
procedure _Truncate(var f: TFileRec);
|
|
function _ValLong(const s: String; var code: Integer): Longint;
|
|
{$IFDEF LINUX}
|
|
procedure _UnhandledException;
|
|
{$ENDIF}
|
|
function _WriteRec(var f: TFileRec; buffer: Pointer): Pointer;
|
|
function _WriteChar(var t: TTextRec; c: Char; width: Integer): Pointer;
|
|
function _Write0Char(var t: TTextRec; c: Char): Pointer;
|
|
function _WriteBool(var t: TTextRec; val: Boolean; width: Longint): Pointer;
|
|
function _Write0Bool(var t: TTextRec; val: Boolean): Pointer;
|
|
function _WriteLong(var t: TTextRec; val, width: Longint): Pointer;
|
|
function _Write0Long(var t: TTextRec; val: Longint): Pointer;
|
|
function _WriteString(var t: TTextRec; const s: ShortString; width: Longint): Pointer;
|
|
function _Write0String(var t: TTextRec; const s: ShortString): Pointer;
|
|
function _WriteCString(var t: TTextRec; s: PChar; width: Longint): Pointer;
|
|
function _Write0CString(var t: TTextRec; s: PChar): Pointer;
|
|
function _Write0LString(var t: TTextRec; const s: AnsiString): Pointer;
|
|
function _WriteLString(var t: TTextRec; const s: AnsiString; width: Longint): Pointer;
|
|
function _Write0WString(var t: TTextRec; const s: WideString): Pointer;
|
|
function _WriteWString(var t: TTextRec; const s: WideString; width: Longint): Pointer;
|
|
function _WriteWCString(var t: TTextRec; s: PWideChar; width: Longint): Pointer;
|
|
function _Write0WCString(var t: TTextRec; s: PWideChar): Pointer;
|
|
function _WriteWChar(var t: TTextRec; c: WideChar; width: Integer): Pointer;
|
|
function _Write0WChar(var t: TTextRec; c: WideChar): Pointer;
|
|
procedure _Write2Ext;
|
|
procedure _Write1Ext;
|
|
procedure _Write0Ext;
|
|
function _WriteLn(var t: TTextRec): Pointer;
|
|
|
|
procedure __CToPasStr(Dest: PShortString; const Source: PChar);
|
|
procedure __CLenToPasStr(Dest: PShortString; const Source: PChar; MaxLen: Integer);
|
|
procedure __ArrayToPasStr(Dest: PShortString; const Source: PChar; Len: Integer);
|
|
procedure __PasToCStr(const Source: PShortString; const Dest: PChar);
|
|
|
|
procedure __IOTest;
|
|
function _Flush(var t: TTextRec): Integer;
|
|
|
|
procedure _SetElem;
|
|
procedure _SetRange;
|
|
procedure _SetEq;
|
|
procedure _SetLe;
|
|
procedure _SetIntersect;
|
|
procedure _SetIntersect3; { BEG only }
|
|
procedure _SetUnion;
|
|
procedure _SetUnion3; { BEG only }
|
|
procedure _SetSub;
|
|
procedure _SetSub3; { BEG only }
|
|
procedure _SetExpand;
|
|
|
|
procedure _Str2Ext;
|
|
procedure _Str0Ext;
|
|
procedure _Str1Ext;
|
|
procedure _ValExt;
|
|
procedure _Pow10;
|
|
procedure _Real2Ext;
|
|
procedure _Ext2Real;
|
|
|
|
procedure _ObjSetup;
|
|
procedure _ObjCopy;
|
|
procedure _Fail;
|
|
procedure _BoundErr;
|
|
procedure _IntOver;
|
|
|
|
{ Module initialization context. For internal use only. }
|
|
|
|
type
|
|
PInitContext = ^TInitContext;
|
|
TInitContext = record
|
|
OuterContext: PInitContext; { saved InitContext }
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
ExcFrame: Pointer; { bottom exc handler }
|
|
{$ENDIF}
|
|
InitTable: PackageInfo; { unit init info }
|
|
InitCount: Integer; { how far we got }
|
|
Module: PLibModule; { ptr to module desc }
|
|
DLLSaveEBP: Pointer; { saved regs for DLLs }
|
|
DLLSaveEBX: Pointer; { saved regs for DLLs }
|
|
DLLSaveESI: Pointer; { saved regs for DLLs }
|
|
DLLSaveEDI: Pointer; { saved regs for DLLs }
|
|
{$IFDEF MSWINDOWS}
|
|
ExitProcessTLS: procedure; { Shutdown for TLS }
|
|
{$ENDIF}
|
|
DLLInitState: Byte; { 0 = package, 1 = DLL shutdown, 2 = DLL startup }
|
|
end platform;
|
|
|
|
type
|
|
TDLLProc = procedure (Reason: Integer);
|
|
// TDLLProcEx provides the reserved param returned by WinNT
|
|
TDLLProcEx = procedure (Reason: Integer; Reserved: Integer);
|
|
|
|
{$IFDEF LINUX}
|
|
procedure _StartExe(InitTable: PackageInfo; Module: PLibModule; Argc: Integer; Argv: Pointer);
|
|
procedure _StartLib(Context: PInitContext; Module: PLibModule; DLLProc: TDLLProcEx);
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
procedure _StartExe(InitTable: PackageInfo; Module: PLibModule);
|
|
procedure _StartLib;
|
|
{$ENDIF}
|
|
procedure _PackageLoad(const Table : PackageInfo; Module: PLibModule);
|
|
procedure _PackageUnload(const Table : PackageInfo; Module: PLibModule);
|
|
procedure _InitResStrings;
|
|
procedure _InitResStringImports;
|
|
procedure _InitImports;
|
|
{$IFDEF MSWINDOWS}
|
|
procedure _InitWideStrings;
|
|
{$ENDIF}
|
|
|
|
function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
|
|
procedure _ClassDestroy(Instance: TObject);
|
|
function _AfterConstruction(Instance: TObject): TObject;
|
|
function _BeforeDestruction(Instance: TObject; OuterMost: ShortInt): TObject;
|
|
function _IsClass(Child: TObject; Parent: TClass): Boolean;
|
|
function _AsClass(Child: TObject; Parent: TClass): TObject;
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure _RaiseAtExcept;
|
|
//procedure _DestroyException(Exc: PRaisedException);
|
|
procedure _DestroyException;
|
|
{$ENDIF}
|
|
procedure _RaiseExcept;
|
|
procedure _RaiseAgain;
|
|
procedure _DoneExcept;
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure _TryFinallyExit;
|
|
{$ENDIF}
|
|
procedure _HandleAnyException;
|
|
procedure _HandleFinally;
|
|
procedure _HandleOnException;
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure _HandleOnExceptionPIC;
|
|
{$ENDIF}
|
|
procedure _HandleAutoException;
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure _ClassHandleException;
|
|
{$ENDIF}
|
|
|
|
procedure _CallDynaInst;
|
|
procedure _CallDynaClass;
|
|
procedure _FindDynaInst;
|
|
procedure _FindDynaClass;
|
|
|
|
procedure _LStrClr(var S);
|
|
procedure _LStrArrayClr(var StrArray; cnt: longint);
|
|
procedure _LStrAsg(var dest; const source);
|
|
procedure _LStrLAsg(var dest; const source);
|
|
procedure _LStrFromPCharLen(var Dest: AnsiString; Source: PAnsiChar; Length: Integer);
|
|
procedure _LStrFromPWCharLen(var Dest: AnsiString; Source: PWideChar; Length: Integer);
|
|
procedure _LStrFromChar(var Dest: AnsiString; Source: AnsiChar);
|
|
procedure _LStrFromWChar(var Dest: AnsiString; Source: WideChar);
|
|
procedure _LStrFromPChar(var Dest: AnsiString; Source: PAnsiChar);
|
|
procedure _LStrFromPWChar(var Dest: AnsiString; Source: PWideChar);
|
|
procedure _LStrFromString(var Dest: AnsiString; const Source: ShortString);
|
|
procedure _LStrFromArray(var Dest: AnsiString; Source: PAnsiChar; Length: Integer);
|
|
procedure _LStrFromWArray(var Dest: AnsiString; Source: PWideChar; Length: Integer);
|
|
procedure _LStrFromWStr(var Dest: AnsiString; const Source: WideString);
|
|
procedure _LStrToString{(var Dest: ShortString; const Source: AnsiString; MaxLen: Integer)};
|
|
function _LStrLen(const s: AnsiString): Longint;
|
|
procedure _LStrCat{var dest: AnsiString; source: AnsiString};
|
|
procedure _LStrCat3{var dest:AnsiString; source1: AnsiString; source2: AnsiString};
|
|
procedure _LStrCatN{var dest:AnsiString; argCnt: Integer; ...};
|
|
procedure _LStrCmp{left: AnsiString; right: AnsiString};
|
|
function _LStrAddRef(var str): Pointer;
|
|
function _LStrToPChar(const s: AnsiString): PChar;
|
|
procedure _Copy{ s : ShortString; index, count : Integer ) : ShortString};
|
|
procedure _Delete{ var s : openstring; index, count : Integer };
|
|
procedure _Insert{ source : ShortString; var s : openstring; index : Integer };
|
|
function Pos(const substr, str: AnsiString): Integer; overload;
|
|
function Pos(const substr, str: WideString): Integer; overload;
|
|
procedure _SetLength(s: PShortString; newLength: Byte);
|
|
procedure _SetString(s: PShortString; buffer: PChar; len: Byte);
|
|
|
|
procedure UniqueString(var str: AnsiString); overload;
|
|
procedure UniqueString(var str: WideString); overload;
|
|
procedure _UniqueStringA(var str: AnsiString);
|
|
procedure _UniqueStringW(var str: WideString);
|
|
|
|
|
|
procedure _LStrCopy { const s : AnsiString; index, count : Integer) : AnsiString};
|
|
procedure _LStrDelete{ var s : AnsiString; index, count : Integer };
|
|
procedure _LStrInsert{ const source : AnsiString; var s : AnsiString; index : Integer };
|
|
procedure _LStrPos{ const substr : AnsiString; const s : AnsiString ) : Integer};
|
|
procedure _LStrSetLength{ var str: AnsiString; newLength: Integer};
|
|
procedure _LStrOfChar{ c: Char; count: Integer): AnsiString };
|
|
function _NewAnsiString(length: Longint): Pointer; { for debugger purposes only }
|
|
function _NewWideString(CharLength: Longint): Pointer;
|
|
|
|
procedure _WStrClr(var S);
|
|
procedure _WStrArrayClr(var StrArray; Count: Integer);
|
|
procedure _WStrAsg(var Dest: WideString; const Source: WideString);
|
|
procedure _WStrLAsg(var Dest: WideString; const Source: WideString);
|
|
function _WStrToPWChar(const S: WideString): PWideChar;
|
|
function _WStrLen(const S: WideString): Integer;
|
|
procedure _WStrFromPCharLen(var Dest: WideString; Source: PAnsiChar; Length: Integer);
|
|
procedure _WStrFromPWCharLen(var Dest: WideString; Source: PWideChar; CharLength: Integer);
|
|
procedure _WStrFromChar(var Dest: WideString; Source: AnsiChar);
|
|
procedure _WStrFromWChar(var Dest: WideString; Source: WideChar);
|
|
procedure _WStrFromPChar(var Dest: WideString; Source: PAnsiChar);
|
|
procedure _WStrFromPWChar(var Dest: WideString; Source: PWideChar);
|
|
procedure _WStrFromString(var Dest: WideString; const Source: ShortString);
|
|
procedure _WStrFromArray(var Dest: WideString; Source: PAnsiChar; Length: Integer);
|
|
procedure _WStrFromWArray(var Dest: WideString; Source: PWideChar; Length: Integer);
|
|
procedure _WStrFromLStr(var Dest: WideString; const Source: AnsiString);
|
|
procedure _WStrToString(Dest: PShortString; const Source: WideString; MaxLen: Integer);
|
|
procedure _WStrCat(var Dest: WideString; const Source: WideString);
|
|
procedure _WStrCat3(var Dest: WideString; const Source1, Source2: WideString);
|
|
procedure _WStrCatN{var dest:WideString; argCnt: Integer; ...};
|
|
procedure _WStrCmp{left: WideString; right: WideString};
|
|
function _WStrCopy(const S: WideString; Index, Count: Integer): WideString;
|
|
procedure _WStrDelete(var S: WideString; Index, Count: Integer);
|
|
procedure _WStrInsert(const Source: WideString; var Dest: WideString; Index: Integer);
|
|
procedure _WStrPos{ const substr : WideString; const s : WideString ) : Integer};
|
|
procedure _WStrSetLength(var S: WideString; NewLength: Integer);
|
|
function _WStrOfWChar(Ch: WideChar; Count: Integer): WideString;
|
|
function _WStrAddRef(var str: WideString): Pointer;
|
|
|
|
procedure _Initialize(p: Pointer; typeInfo: Pointer);
|
|
procedure _InitializeArray(p: Pointer; typeInfo: Pointer; elemCount: Cardinal);
|
|
procedure _InitializeRecord(p: Pointer; typeInfo: Pointer);
|
|
procedure _Finalize(p: Pointer; typeInfo: Pointer);
|
|
procedure _FinalizeArray(p: Pointer; typeInfo: Pointer; elemCount: Cardinal);
|
|
procedure _FinalizeRecord(P: Pointer; typeInfo: Pointer);
|
|
procedure _AddRef;
|
|
procedure _AddRefArray;
|
|
procedure _AddRefRecord;
|
|
procedure _CopyArray;
|
|
procedure _CopyRecord;
|
|
procedure _CopyObject;
|
|
|
|
function _New(size: Longint; typeInfo: Pointer): Pointer;
|
|
procedure _Dispose(p: Pointer; typeInfo: Pointer);
|
|
|
|
{ 64-bit Integer helper routines }
|
|
procedure __llmul;
|
|
procedure __lldiv;
|
|
procedure __lludiv;
|
|
procedure __llmod;
|
|
procedure __llmulo;
|
|
procedure __lldivo;
|
|
procedure __llmodo;
|
|
procedure __llumod;
|
|
procedure __llshl;
|
|
procedure __llushr;
|
|
procedure _WriteInt64;
|
|
procedure _Write0Int64;
|
|
procedure _ReadInt64;
|
|
function _StrInt64(val: Int64; width: Integer): ShortString;
|
|
function _Str0Int64(val: Int64): ShortString;
|
|
function _ValInt64(const s: AnsiString; var code: Integer): Int64;
|
|
|
|
{ Dynamic array helper functions }
|
|
|
|
procedure _DynArrayHigh;
|
|
procedure _DynArrayClear(var a: Pointer; typeInfo: Pointer);
|
|
procedure _DynArrayLength;
|
|
procedure _DynArraySetLength;
|
|
procedure _DynArrayCopy(a: Pointer; typeInfo: Pointer; var Result: Pointer);
|
|
procedure _DynArrayCopyRange(a: Pointer; typeInfo: Pointer; index, count : Integer; var Result: Pointer);
|
|
procedure _DynArrayAsg;
|
|
procedure _DynArrayAddRef;
|
|
|
|
procedure DynArrayClear(var a: Pointer; typeInfo: Pointer);
|
|
procedure DynArraySetLength(var a: Pointer; typeInfo: Pointer; dimCnt: Longint; lengthVec: PLongint);
|
|
function DynArrayDim(typeInfo: PDynArrayTypeInfo): Integer;
|
|
{$NODEFINE DynArrayDim}
|
|
|
|
function _IntfClear(var Dest: IInterface): Pointer;
|
|
procedure _IntfCopy(var Dest: IInterface; const Source: IInterface);
|
|
procedure _IntfCast(var Dest: IInterface; const Source: IInterface; const IID: TGUID);
|
|
procedure _IntfAddRef(const Dest: IInterface);
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
procedure _FSafeDivide;
|
|
procedure _FSafeDivideR;
|
|
{$ENDIF}
|
|
|
|
function _CheckAutoResult(ResultCode: HResult): HResult;
|
|
|
|
procedure FPower10;
|
|
|
|
procedure TextStart; deprecated;
|
|
|
|
// Conversion utility routines for C++ convenience. Not for Delphi code.
|
|
function CompToDouble(Value: Comp): Double; cdecl;
|
|
procedure DoubleToComp(Value: Double; var Result: Comp); cdecl;
|
|
function CompToCurrency(Value: Comp): Currency; cdecl;
|
|
procedure CurrencyToComp(Value: Currency; var Result: Comp); cdecl;
|
|
|
|
function GetMemory(Size: Integer): Pointer; cdecl;
|
|
function FreeMemory(P: Pointer): Integer; cdecl;
|
|
function ReallocMemory(P: Pointer; Size: Integer): Pointer; cdecl;
|
|
|
|
{ Internal runtime error codes }
|
|
|
|
type
|
|
TRuntimeError = (reNone, reOutOfMemory, reInvalidPtr, reDivByZero,
|
|
reRangeError, reIntOverflow, reInvalidOp, reZeroDivide, reOverflow,
|
|
reUnderflow, reInvalidCast, reAccessViolation, rePrivInstruction,
|
|
reControlBreak, reStackOverflow,
|
|
{ reVar* used in Variants.pas }
|
|
reVarTypeCast, reVarInvalidOp,
|
|
reVarDispatch, reVarArrayCreate, reVarNotArray, reVarArrayBounds,
|
|
reAssertionFailed,
|
|
reExternalException, { not used here; in SysUtils }
|
|
reIntfCastError, reSafeCallError);
|
|
{$NODEFINE TRuntimeError}
|
|
|
|
procedure Error(errorCode: TRuntimeError);
|
|
{$NODEFINE Error}
|
|
|
|
{ GetLastError returns the last error reported by an OS API call. Calling
|
|
this function usually resets the OS error state.
|
|
}
|
|
|
|
function GetLastError: Integer; {$IFDEF MSWINDOWS} stdcall; {$ENDIF}
|
|
{$EXTERNALSYM GetLastError}
|
|
|
|
{ SetLastError writes to the thread local storage area read by GetLastError. }
|
|
|
|
procedure SetLastError(ErrorCode: Integer); {$IFDEF MSWINDOWS} stdcall; {$ENDIF}
|
|
|
|
{$IFDEF LINUX}
|
|
{ To improve performance, some RTL routines cache module handles and data
|
|
derived from modules. If an application dynamically loads and unloads
|
|
shared object libraries, packages, or resource packages, it is possible for
|
|
the handle of the newly loaded module to match the handle of a recently
|
|
unloaded module. The resource caches have no way to detect when this happens.
|
|
|
|
To address this issue, the RTL maintains an internal counter that is
|
|
incremented every time a module is loaded or unloaded using RTL functions
|
|
(like LoadPackage). This provides a cache version level signature that
|
|
can detect when modules have been cycled but have the same handle.
|
|
|
|
If you load or unload modules "by hand" using dlopen or dlclose, you must call
|
|
InvalidateModuleCache after each load or unload so that the RTL module handle
|
|
caches will refresh themselves properly the next time they are used. This is
|
|
especially important if you manually tinker with the LibModuleList list of
|
|
loaded modules, or manually add or remove resource modules in the nodes
|
|
of that list.
|
|
|
|
ModuleCacheID returns the "current generation" or version number kept by
|
|
the RTL. You can use this to implement your own refresh-on-next-use
|
|
(passive) module handle caches as the RTL does. The value changes each
|
|
time InvalidateModuleCache is called.
|
|
}
|
|
|
|
function ModuleCacheID: Cardinal;
|
|
procedure InvalidateModuleCache;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF LINUX}
|
|
{ When a process that is being debugged is stopped while it has the mouse
|
|
pointer grabbed, there is no way for the debugger to release the grab on
|
|
behalf of the process. The process needs to do it itself. To accomplish this,
|
|
the debugger causes DbgUnlockX to execute whenever it detects the process
|
|
might have the mouse grabbed. This method will call through DbgUnlockXProc
|
|
which should be assigned by any library using X and locks the X pointer. This
|
|
method should be chained, by storing of the previous instance and calling it
|
|
when you are called, since there might be more than one display that needs
|
|
to be unlocked. This method should call XUngrabPointer on the display that
|
|
has the pointer grabbed.
|
|
}
|
|
var
|
|
DbgUnlockXProc: procedure;
|
|
|
|
procedure DbgUnlockX;
|
|
{$ENDIF}
|
|
|
|
{X+ moved here from SysInit.pas - by advise of Alexey Torgashin - to avoid
|
|
creating of separate import block from kernel32.dll : }
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
function FreeLibrary(ModuleHandle: Longint): LongBool; stdcall;
|
|
function GetModuleFileName(Module: Integer; Filename: PChar; Size: Integer): Integer; stdcall;
|
|
function GetModuleHandle(ModuleName: PChar): Integer; stdcall;
|
|
function LocalAlloc(flags, size: Integer): Pointer; stdcall;
|
|
function LocalFree(addr: Pointer): Pointer; stdcall;
|
|
function TlsAlloc: Integer; stdcall;
|
|
function TlsFree(TlsIndex: Integer): Boolean; stdcall;
|
|
function TlsGetValue(TlsIndex: Integer): Pointer; stdcall;
|
|
function TlsSetValue(TlsIndex: Integer; TlsValue: Pointer): Boolean; stdcall;
|
|
function GetCommandLine: PChar; stdcall;
|
|
{X-}//////////////////////////////////////////////////////////////////////
|
|
|
|
{X+}
|
|
{X}function GetProcessHeap: THandle; stdcall;
|
|
{X}function HeapAlloc(hHeap: THandle; dwFlags, dwBytes: Cardinal): Pointer; stdcall;
|
|
{X}function HeapReAlloc(hHeap: THandle; dwFlags: Cardinal; lpMem: Pointer; dwBytes: Cardinal): Pointer; stdcall;
|
|
{X}function HeapFree(hHeap: THandle; dwFlags: Cardinal; lpMem: Pointer): LongBool; stdcall;
|
|
{X}function DfltGetMem(size: Integer): Pointer;
|
|
{X}function DfltFreeMem(p: Pointer): Integer;
|
|
{X}function DfltReallocMem(p: Pointer; size: Integer): Pointer;
|
|
{X} procedure InitUnitsLight( Table : PUnitEntryTable; Idx, Count : Integer );
|
|
{X} procedure FInitUnitsLight;
|
|
{X} // following two procedures are optional and exclusive.
|
|
{X} // call it to provide error message: first - for GUI app,
|
|
{X} // second - for console app.
|
|
{X} procedure UseErrorMessageBox;
|
|
{X} procedure UseErrorMessageWrite;
|
|
|
|
{X} // call following procedure to initialize Input and Output
|
|
{X} // - for console app only:
|
|
{X} procedure UseInputOutput;
|
|
|
|
{X} // if your app uses FPU, call one of following procedures:
|
|
{X} procedure FpuInit;
|
|
{X} procedure FpuInitConsiderNECWindows;
|
|
{X} // the second additionally takes into consideration NEC
|
|
{X} // Windows keyboard (Japaneeze keyboard ???).
|
|
|
|
{X} procedure DummyProc; // empty procedure
|
|
|
|
(*
|
|
{X} procedure VariantClr;
|
|
{X} // procedure to refer to _VarClr if SysVarnt.pas is in use
|
|
{X} var VarClrProc : procedure = DummyProc;
|
|
|
|
{X} procedure VarCastError;
|
|
{X} procedure VarInvalidOp;
|
|
|
|
{X} procedure VariantAddRef;
|
|
{X} // procedure to refer to _VarAddRef if SysVarnt.pas is in use
|
|
{X} var VarAddRefProc : procedure = DummyProc;
|
|
*)
|
|
|
|
{X} procedure WStrAddRef;
|
|
{X} // procedure to refer to _WStrAddRef if SysWStr.pas is in use
|
|
{X} var WStrAddRefProc : procedure = DummyProc;
|
|
|
|
{X} procedure WStrClr;
|
|
{X} // procedure to refer to _WStrClr if SysWStr.pas is in use
|
|
{X} var WStrClrProc : procedure = DummyProc;
|
|
|
|
{X} procedure WStrArrayClr;
|
|
{X} // procedure to refer to _WStrArrayClr if SysWStr.pas is in use
|
|
{X} var WStrArrayClrProc : procedure = DummyProc;
|
|
|
|
{X} // Standard Delphi units initialization/finalization uses
|
|
{X} // try-except and raise constructions, which leads to permanent
|
|
{X} // usage of all exception handling routines. In this XCL-aware
|
|
{X} // implementation, "light" version of initialization/finalization
|
|
{X} // is used by default. To use standard Delphi initialization and
|
|
{X} // finalization method, allowing to flow execution control even
|
|
{X} // in initalization sections, include reference to SysSfIni.pas
|
|
{X} // into uses clause *as first as possible*.
|
|
{X} procedure InitUnitsHard( Table : PUnitEntryTable; Idx, Count : Integer );
|
|
{X} var InitUnitsProc : procedure( Table : PUnitEntryTable; Idx, Count : Integer )
|
|
{X} = InitUnitsLight;
|
|
{X} procedure FInitUnitsHard;
|
|
{X} var FInitUnitsProc : procedure = FInitUnitsLight;
|
|
{X} procedure SetExceptionHandler;
|
|
{X} procedure UnsetExceptionHandler;
|
|
{X} var UnsetExceptionHandlerProc : procedure = DummyProc;
|
|
|
|
{X} var UnloadResProc: procedure = DummyProc;
|
|
{X-}
|
|
|
|
(* =================================================================== *)
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysInit;
|
|
|
|
{ This procedure should be at the very beginning of the }
|
|
{ text segment. It used to be used by _RunError to find }
|
|
{ start address of the text segment, but is not used anymore. }
|
|
|
|
procedure TextStart;
|
|
begin
|
|
end;
|
|
|
|
{X+}
|
|
const
|
|
advapi32 = 'advapi32.dll';
|
|
kernel = 'kernel32.dll';
|
|
user = 'user32.dll';
|
|
oleaut = 'oleaut32.dll';
|
|
|
|
function GetProcessHeap; external kernel name 'GetProcessHeap';
|
|
function HeapAlloc; stdcall; external kernel name 'HeapAlloc';
|
|
function HeapReAlloc; stdcall; external kernel name 'HeapReAlloc';
|
|
function HeapFree; stdcall; external kernel name 'HeapFree';
|
|
{X-}
|
|
|
|
{$IFDEF PIC}
|
|
function GetGOT: LongWord; export;
|
|
begin
|
|
asm
|
|
MOV Result,EBX
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
const
|
|
UNWINDFI_TOPOFSTACK = $BE00EF00;
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
const
|
|
unwind = 'unwind.dll';
|
|
|
|
type
|
|
UNWINDPROC = Pointer;
|
|
function UnwindRegisterIPLookup(fn: UNWINDPROC; StartAddr, EndAddr: LongInt; Context: Pointer; GOT: LongInt): LongBool; cdecl;
|
|
external unwind name '__BorUnwind_RegisterIPLookup';
|
|
|
|
function UnwindDelphiLookup(Addr: LongInt; Context: Pointer): UNWINDPROC; cdecl;
|
|
external unwind name '__BorUnwind_DelphiLookup';
|
|
|
|
function UnwindRaiseException(Exc: Pointer): LongBool; cdecl;
|
|
external unwind name '__BorUnwind_RaiseException';
|
|
|
|
function UnwindClosestHandler(Context: Pointer): LongWord; cdecl;
|
|
external unwind name '__BorUnwind_ClosestDelphiHandler';
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
const
|
|
unwind = 'libborunwind.so.6';
|
|
type
|
|
UNWINDPROC = Pointer;
|
|
|
|
{$DEFINE STATIC_UNWIND}
|
|
|
|
{$IFDEF STATIC_UNWIND}
|
|
function _BorUnwind_RegisterIPLookup(fn: UNWINDPROC; StartAddr, EndAddr: LongInt; Context: Pointer; GOT: LongInt): LongBool; cdecl;
|
|
external;
|
|
|
|
procedure _BorUnwind_UnregisterIPLookup(StartAddr: LongInt); cdecl; external;
|
|
|
|
function _BorUnwind_DelphiLookup(Addr: LongInt; Context: Pointer): UNWINDPROC; cdecl; external;
|
|
|
|
function _BorUnwind_RaiseException(Exc: Pointer): LongBool; cdecl; external;
|
|
|
|
//function _BorUnwind_AddressIsInPCMap(Addr: LongInt): LongBool; cdecl; external;
|
|
function _BorUnwind_ClosestDelphiHandler(Context: Pointer): LongWord; cdecl; external;
|
|
{$ELSE}
|
|
|
|
function _BorUnwind_RegisterIPLookup(fn: UNWINDPROC; StartAddr, EndAddr: LongInt; Context: Pointer; GOT: LongInt): LongBool; cdecl;
|
|
external unwind name '_BorUnwind_RegisterIPLookup';
|
|
|
|
procedure _BorUnwind_UnregisterIPLookup(StartAddr: LongInt); cdecl;
|
|
external unwind name '_BorUnwind_UnregisterIPLookup';
|
|
|
|
function _BorUnwind_DelphiLookup(Addr: LongInt; Context: Pointer): UNWINDPROC; cdecl;
|
|
external unwind name '_BorUnwind_DelphiLookup';
|
|
|
|
function _BorUnwind_RaiseException(Exc: Pointer): LongBool; cdecl;
|
|
external unwind name '_BorUnwind_RaiseException';
|
|
|
|
function _BorUnwind_ClosestDelphiHandler(Context: Pointer): LongWord; cdecl;
|
|
external unwind name '_BorUnwind_ClosestDelphiHandler';
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
const { copied from xx.h }
|
|
cContinuable = 0;
|
|
cNonContinuable = 1;
|
|
cUnwinding = 2;
|
|
cUnwindingForExit = 4;
|
|
cUnwindInProgress = cUnwinding or cUnwindingForExit;
|
|
cDelphiException = $0EEDFADE;
|
|
cDelphiReRaise = $0EEDFADF;
|
|
cDelphiExcept = $0EEDFAE0;
|
|
cDelphiFinally = $0EEDFAE1;
|
|
cDelphiTerminate = $0EEDFAE2;
|
|
cDelphiUnhandled = $0EEDFAE3;
|
|
cNonDelphiException = $0EEDFAE4;
|
|
cDelphiExitFinally = $0EEDFAE5;
|
|
cCppException = $0EEFFACE; { used by BCB }
|
|
EXCEPTION_CONTINUE_SEARCH = 0;
|
|
EXCEPTION_EXECUTE_HANDLER = 1;
|
|
EXCEPTION_CONTINUE_EXECUTION = -1;
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
const
|
|
excIsBeingHandled = $00000001;
|
|
excIsBeingReRaised = $00000002;
|
|
{$ENDIF}
|
|
|
|
type
|
|
JmpInstruction =
|
|
packed record
|
|
opCode: Byte;
|
|
distance: Longint;
|
|
end;
|
|
TExcDescEntry =
|
|
record
|
|
vTable: Pointer;
|
|
handler: Pointer;
|
|
end;
|
|
PExcDesc = ^TExcDesc;
|
|
TExcDesc =
|
|
packed record
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
jmp: JmpInstruction;
|
|
{$ENDIF}
|
|
case Integer of
|
|
0: (instructions: array [0..0] of Byte);
|
|
1{...}: (cnt: Integer; excTab: array [0..0{cnt-1}] of TExcDescEntry);
|
|
end;
|
|
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
PExcFrame = ^TExcFrame;
|
|
TExcFrame = record
|
|
next: PExcFrame;
|
|
desc: PExcDesc;
|
|
hEBP: Pointer;
|
|
case Integer of
|
|
0: ( );
|
|
1: ( ConstructedObject: Pointer );
|
|
2: ( SelfOfMethod: Pointer );
|
|
end;
|
|
|
|
PExceptionRecord = ^TExceptionRecord;
|
|
TExceptionRecord =
|
|
record
|
|
ExceptionCode : LongWord;
|
|
ExceptionFlags : LongWord;
|
|
OuterException : PExceptionRecord;
|
|
ExceptionAddress : Pointer;
|
|
NumberParameters : Longint;
|
|
case {IsOsException:} Boolean of
|
|
True: (ExceptionInformation : array [0..14] of Longint);
|
|
False: (ExceptAddr: Pointer; ExceptObject: Pointer);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
PRaisedException = ^TRaisedException;
|
|
TRaisedException = packed record
|
|
RefCount: Integer;
|
|
ExceptObject: TObject;
|
|
ExceptionAddr: Pointer;
|
|
HandlerEBP: LongWord;
|
|
Flags: LongWord;
|
|
end;
|
|
{$ELSE}
|
|
PRaiseFrame = ^TRaiseFrame;
|
|
TRaiseFrame = packed record
|
|
NextRaise: PRaiseFrame;
|
|
ExceptAddr: Pointer;
|
|
ExceptObject: TObject;
|
|
ExceptionRecord: PExceptionRecord;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
const
|
|
cCR = $0D;
|
|
cLF = $0A;
|
|
cEOF = $1A;
|
|
|
|
{$IFDEF LINUX}
|
|
const
|
|
libc = 'libc.so.6';
|
|
libdl = 'libdl.so.2';
|
|
libpthread = 'libpthread.so.0';
|
|
|
|
O_RDONLY = $0000;
|
|
O_WRONLY = $0001;
|
|
O_RDWR = $0002;
|
|
O_CREAT = $0040;
|
|
O_EXCL = $0080;
|
|
O_NOCTTY = $0100;
|
|
O_TRUNC = $0200;
|
|
O_APPEND = $0400;
|
|
|
|
// protection flags
|
|
S_IREAD = $0100; // Read by owner.
|
|
S_IWRITE = $0080; // Write by owner.
|
|
S_IEXEC = $0040; // Execute by owner.
|
|
S_IRUSR = S_IREAD;
|
|
S_IWUSR = S_IWRITE;
|
|
S_IXUSR = S_IEXEC;
|
|
S_IRWXU = S_IRUSR or S_IWUSR or S_IXUSR;
|
|
|
|
S_IRGRP = S_IRUSR shr 3; // Read by group.
|
|
S_IWGRP = S_IWUSR shr 3; // Write by group.
|
|
S_IXGRP = S_IXUSR shr 3; // Execute by group.
|
|
S_IRWXG = S_IRWXU shr 3; // Read, write, and execute by group.
|
|
|
|
S_IROTH = S_IRGRP shr 3; // Read by others.
|
|
S_IWOTH = S_IWGRP shr 3; // Write by others.
|
|
S_IXOTH = S_IXGRP shr 3; // Execute by others.
|
|
S_IRWXO = S_IRWXG shr 3; // Read, write, and execute by others.
|
|
|
|
STDIN_FILENO = 0;
|
|
STDOUT_FILENO = 1;
|
|
STDERR_FILENO = 2;
|
|
|
|
SEEK_SET = 0;
|
|
SEEK_CUR = 1;
|
|
SEEK_END = 2;
|
|
|
|
LC_CTYPE = 0;
|
|
_NL_CTYPE_CODESET_NAME = LC_CTYPE shl 16 + 14;
|
|
|
|
MAX_PATH = 4095;
|
|
|
|
|
|
function __open(PathName: PChar; Flags: Integer; Mode: Integer): Integer; cdecl;
|
|
external libc name 'open';
|
|
|
|
function __close(Handle: Integer): Integer; cdecl;
|
|
external libc name 'close';
|
|
|
|
function __read(Handle: Integer; Buffer: Pointer; Count: Cardinal): Cardinal; cdecl;
|
|
external libc name 'read';
|
|
|
|
function __write(Handle: Integer; Buffer: Pointer; Count: Cardinal): Cardinal; cdecl;
|
|
external libc name 'write';
|
|
|
|
function __mkdir(PathName: PChar; Mode: Integer): Integer; cdecl;
|
|
external libc name 'mkdir';
|
|
|
|
function __getcwd(Buffer: PChar; BufSize: Integer): PChar; cdecl;
|
|
external libc name 'getcwd';
|
|
|
|
function __getenv(Name: PChar): PChar; cdecl;
|
|
external libc name 'getenv';
|
|
|
|
function __chdir(PathName: PChar): Integer; cdecl;
|
|
external libc name 'chdir';
|
|
|
|
function __rmdir(PathName: PChar): Integer; cdecl;
|
|
external libc name 'rmdir';
|
|
|
|
function __remove(PathName: PChar): Integer; cdecl;
|
|
external libc name 'remove';
|
|
|
|
function __rename(OldPath, NewPath: PChar): Integer; cdecl;
|
|
external libc name 'rename';
|
|
|
|
{$IFDEF EFENCE}
|
|
function __malloc(Size: Integer): Pointer; cdecl;
|
|
external 'libefence.so' name 'malloc';
|
|
|
|
procedure __free(P: Pointer); cdecl;
|
|
external 'libefence.so' name 'free';
|
|
|
|
function __realloc(P: Pointer; Size: Integer): Pointer; cdecl;
|
|
external 'libefence.so' name 'realloc';
|
|
{$ELSE}
|
|
function __malloc(Size: Integer): Pointer; cdecl;
|
|
external libc name 'malloc';
|
|
|
|
procedure __free(P: Pointer); cdecl;
|
|
external libc name 'free';
|
|
|
|
function __realloc(P: Pointer; Size: Integer): Pointer; cdecl;
|
|
external libc name 'realloc';
|
|
{$ENDIF}
|
|
|
|
procedure ExitProcess(status: Integer); cdecl;
|
|
external libc name 'exit';
|
|
|
|
function _time(P: Pointer): Integer; cdecl;
|
|
external libc name 'time';
|
|
|
|
function _lseek(Handle, Offset, Direction: Integer): Integer; cdecl;
|
|
external libc name 'lseek';
|
|
|
|
function _ftruncate(Handle: Integer; Filesize: Integer): Integer; cdecl;
|
|
external libc name 'ftruncate';
|
|
|
|
function strcasecmp(s1, s2: PChar): Integer; cdecl;
|
|
external libc name 'strcasecmp';
|
|
|
|
function __errno_location: PInteger; cdecl;
|
|
external libc name '__errno_location';
|
|
|
|
function nl_langinfo(item: integer): pchar; cdecl;
|
|
external libc name 'nl_langinfo';
|
|
|
|
function iconv_open(ToCode: PChar; FromCode: PChar): Integer; cdecl;
|
|
external libc name 'iconv_open';
|
|
|
|
function iconv(cd: Integer; var InBuf; var InBytesLeft: Integer; var OutBuf; var OutBytesLeft: Integer): Integer; cdecl;
|
|
external libc name 'iconv';
|
|
|
|
function iconv_close(cd: Integer): Integer; cdecl;
|
|
external libc name 'iconv_close';
|
|
|
|
function mblen(const S: PChar; N: LongWord): Integer; cdecl;
|
|
external libc name 'mblen';
|
|
|
|
function mmap(start: Pointer; length: Cardinal; prot, flags, fd, offset: Integer): Pointer; cdecl;
|
|
external libc name 'mmap';
|
|
|
|
function munmap(start: Pointer; length: Cardinal): Integer; cdecl;
|
|
external libc name 'munmap';
|
|
|
|
const
|
|
SIGABRT = 6;
|
|
|
|
function __raise(SigNum: Integer): Integer; cdecl;
|
|
external libc name 'raise';
|
|
|
|
type
|
|
TStatStruct = record
|
|
st_dev: Int64; // device
|
|
__pad1: Word;
|
|
st_ino: Cardinal; // inode
|
|
st_mode: Cardinal; // protection
|
|
st_nlink: Cardinal; // number of hard links
|
|
st_uid: Cardinal; // user ID of owner
|
|
st_gid: Cardinal; // group ID of owner
|
|
st_rdev: Int64; // device type (if inode device)
|
|
__pad2: Word;
|
|
st_size: Cardinal; // total size, in bytes
|
|
st_blksize: Cardinal; // blocksize for filesystem I/O
|
|
st_blocks: Cardinal; // number of blocks allocated
|
|
st_atime: Integer; // time of last access
|
|
__unused1: Cardinal;
|
|
st_mtime: Integer; // time of last modification
|
|
__unused2: Cardinal;
|
|
st_ctime: Integer; // time of last change
|
|
__unused3: Cardinal;
|
|
__unused4: Cardinal;
|
|
__unused5: Cardinal;
|
|
end;
|
|
|
|
const
|
|
STAT_VER_LINUX = 3;
|
|
|
|
function _fxstat(Version: Integer; Handle: Integer; var Stat: TStatStruct): Integer; cdecl;
|
|
external libc name '__fxstat';
|
|
|
|
function __xstat(Ver: Integer; FileName: PChar; var StatBuffer: TStatStruct): Integer; cdecl;
|
|
external libc name '__xstat';
|
|
|
|
function _strlen(P: PChar): Integer; cdecl;
|
|
external libc name 'strlen';
|
|
|
|
function _readlink(PathName: PChar; Buf: PChar; Len: Integer): Integer; cdecl;
|
|
external libc name 'readlink';
|
|
|
|
type
|
|
TDLInfo = record
|
|
FileName: PChar;
|
|
BaseAddress: Pointer;
|
|
NearestSymbolName: PChar;
|
|
SymbolAddress: Pointer;
|
|
end;
|
|
|
|
const
|
|
RTLD_LAZY = 1;
|
|
RTLD_NOW = 2;
|
|
|
|
function dladdr(Address: Pointer; var Info: TDLInfo): Integer; cdecl;
|
|
external libdl name 'dladdr';
|
|
function dlopen(Filename: PChar; Flag: Integer): LongWord; cdecl;
|
|
external libdl name 'dlopen';
|
|
function dlclose(Handle: LongWord): Integer; cdecl;
|
|
external libdl name 'dlclose';
|
|
function FreeLibrary(Handle: LongWord): Integer; cdecl;
|
|
external libdl name 'dlclose';
|
|
function dlsym(Handle: LongWord; Symbol: PChar): Pointer; cdecl;
|
|
external libdl name 'dlsym';
|
|
function dlerror: PChar; cdecl;
|
|
external libdl name 'dlerror';
|
|
|
|
type
|
|
TPthread_fastlock = record
|
|
__status: LongInt;
|
|
__spinlock: Integer;
|
|
end;
|
|
|
|
TRTLCriticalSection = record
|
|
__m_reserved,
|
|
__m_count: Integer;
|
|
__m_owner: Pointer;
|
|
__m_kind: Integer; // __m_kind := 0 fastlock, __m_kind := 1 recursive lock
|
|
__m_lock: TPthread_fastlock;
|
|
end;
|
|
|
|
function _pthread_mutex_lock(var Mutex: TRTLCriticalSection): Integer; cdecl;
|
|
external libpthread name 'pthread_mutex_lock';
|
|
function _pthread_mutex_unlock(var Mutex: TRTLCriticalSection): Integer; cdecl;
|
|
external libpthread name 'pthread_mutex_unlock';
|
|
function _pthread_create(var ThreadID: Cardinal; Attr: PThreadAttr;
|
|
TFunc: TThreadFunc; Arg: Pointer): Integer; cdecl;
|
|
external libpthread name 'pthread_create';
|
|
function _pthread_exit(var RetVal: Integer): Integer; cdecl;
|
|
external libpthread name 'pthread_exit';
|
|
function GetCurrentThreadID: LongWord; cdecl;
|
|
external libpthread name 'pthread_self';
|
|
function _pthread_detach(ThreadID: Cardinal): Integer; cdecl;
|
|
external libpthread name 'pthread_detach'
|
|
|
|
function GetLastError: Integer;
|
|
begin
|
|
Result := __errno_location^;
|
|
end;
|
|
|
|
procedure SetLastError(ErrorCode: Integer);
|
|
begin
|
|
__errno_location^ := ErrorCode;
|
|
end;
|
|
|
|
function InterlockedIncrement(var I: Integer): Integer;
|
|
asm
|
|
MOV EDX,1
|
|
XCHG EAX,EDX
|
|
LOCK XADD [EDX],EAX
|
|
INC EAX
|
|
end;
|
|
|
|
function InterlockedDecrement(var I: Integer): Integer;
|
|
asm
|
|
MOV EDX,-1
|
|
XCHG EAX,EDX
|
|
LOCK XADD [EDX],EAX
|
|
DEC EAX
|
|
end;
|
|
|
|
var
|
|
ModuleCacheVersion: Cardinal = 0;
|
|
|
|
function ModuleCacheID: Cardinal;
|
|
begin
|
|
Result := ModuleCacheVersion;
|
|
end;
|
|
|
|
procedure InvalidateModuleCache;
|
|
begin
|
|
InterlockedIncrement(Integer(ModuleCacheVersion));
|
|
end;
|
|
|
|
{$ENDIF}
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
type
|
|
PMemInfo = ^TMemInfo;
|
|
TMemInfo = packed record
|
|
BaseAddress: Pointer;
|
|
AllocationBase: Pointer;
|
|
AllocationProtect: Longint;
|
|
RegionSize: Longint;
|
|
State: Longint;
|
|
Protect: Longint;
|
|
Type_9 : Longint;
|
|
end;
|
|
|
|
PStartupInfo = ^TStartupInfo;
|
|
TStartupInfo = record
|
|
cb: Longint;
|
|
lpReserved: Pointer;
|
|
lpDesktop: Pointer;
|
|
lpTitle: Pointer;
|
|
dwX: Longint;
|
|
dwY: Longint;
|
|
dwXSize: Longint;
|
|
dwYSize: Longint;
|
|
dwXCountChars: Longint;
|
|
dwYCountChars: Longint;
|
|
dwFillAttribute: Longint;
|
|
dwFlags: Longint;
|
|
wShowWindow: Word;
|
|
cbReserved2: Word;
|
|
lpReserved2: ^Byte;
|
|
hStdInput: Integer;
|
|
hStdOutput: Integer;
|
|
hStdError: Integer;
|
|
end;
|
|
|
|
TWin32FindData = packed record
|
|
dwFileAttributes: Integer;
|
|
ftCreationTime: Int64;
|
|
ftLastAccessTime: Int64;
|
|
ftLastWriteTime: Int64;
|
|
nFileSizeHigh: Integer;
|
|
nFileSizeLow: Integer;
|
|
dwReserved0: Integer;
|
|
dwReserved1: Integer;
|
|
cFileName: array[0..259] of Char;
|
|
cAlternateFileName: array[0..13] of Char;
|
|
end;
|
|
|
|
const
|
|
GENERIC_READ = Integer($80000000);
|
|
GENERIC_WRITE = $40000000;
|
|
FILE_SHARE_READ = $00000001;
|
|
FILE_SHARE_WRITE = $00000002;
|
|
FILE_ATTRIBUTE_NORMAL = $00000080;
|
|
|
|
CREATE_NEW = 1;
|
|
CREATE_ALWAYS = 2;
|
|
OPEN_EXISTING = 3;
|
|
|
|
FILE_BEGIN = 0;
|
|
FILE_CURRENT = 1;
|
|
FILE_END = 2;
|
|
|
|
STD_INPUT_HANDLE = Integer(-10);
|
|
STD_OUTPUT_HANDLE = Integer(-11);
|
|
STD_ERROR_HANDLE = Integer(-12);
|
|
MAX_PATH = 260;
|
|
|
|
{X+ moved here from SysInit.pas - by advise of Alexey Torgashin - to avoid
|
|
creating of separate import block from kernel32.dll : }
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
function FreeLibrary(ModuleHandle: Longint): LongBool; stdcall;
|
|
external kernel name 'FreeLibrary';
|
|
|
|
function GetModuleFileName(Module: Integer; Filename: PChar; Size: Integer): Integer; stdcall;
|
|
external kernel name 'GetModuleFileNameA';
|
|
|
|
function GetModuleHandle(ModuleName: PChar): Integer; stdcall;
|
|
external kernel name 'GetModuleHandleA';
|
|
|
|
function LocalAlloc(flags, size: Integer): Pointer; stdcall;
|
|
external kernel name 'LocalAlloc';
|
|
|
|
function LocalFree(addr: Pointer): Pointer; stdcall;
|
|
external kernel name 'LocalFree';
|
|
|
|
function TlsAlloc: Integer; stdcall;
|
|
external kernel name 'TlsAlloc';
|
|
|
|
function TlsFree(TlsIndex: Integer): Boolean; stdcall;
|
|
external kernel name 'TlsFree';
|
|
|
|
function TlsGetValue(TlsIndex: Integer): Pointer; stdcall;
|
|
external kernel name 'TlsGetValue';
|
|
|
|
function TlsSetValue(TlsIndex: Integer; TlsValue: Pointer): Boolean; stdcall;
|
|
external kernel name 'TlsSetValue';
|
|
|
|
function GetCommandLine: PChar; stdcall;
|
|
external kernel name 'GetCommandLineA';
|
|
{X-}//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
function CloseHandle(Handle: Integer): Integer; stdcall;
|
|
external kernel name 'CloseHandle';
|
|
function CreateFileA(lpFileName: PChar; dwDesiredAccess, dwShareMode: Integer;
|
|
lpSecurityAttributes: Pointer; dwCreationDisposition, dwFlagsAndAttributes: Integer;
|
|
hTemplateFile: Integer): Integer; stdcall;
|
|
external kernel name 'CreateFileA';
|
|
function DeleteFileA(Filename: PChar): LongBool; stdcall;
|
|
external kernel name 'DeleteFileA';
|
|
function GetFileType(hFile: Integer): Integer; stdcall;
|
|
external kernel name 'GetFileType';
|
|
procedure GetSystemTime; stdcall;
|
|
external kernel name 'GetSystemTime';
|
|
function GetFileSize(Handle: Integer; x: Integer): Integer; stdcall;
|
|
external kernel name 'GetFileSize';
|
|
function GetStdHandle(nStdHandle: Integer): Integer; stdcall;
|
|
external kernel name 'GetStdHandle';
|
|
function MoveFileA(OldName, NewName: PChar): LongBool; stdcall;
|
|
external kernel name 'MoveFileA';
|
|
procedure RaiseException; stdcall;
|
|
external kernel name 'RaiseException';
|
|
function ReadFile(hFile: Integer; var Buffer; nNumberOfBytesToRead: Cardinal;
|
|
var lpNumberOfBytesRead: Cardinal; lpOverlapped: Pointer): Integer; stdcall;
|
|
external kernel name 'ReadFile';
|
|
procedure RtlUnwind; stdcall;
|
|
external kernel name 'RtlUnwind';
|
|
function SetEndOfFile(Handle: Integer): LongBool; stdcall;
|
|
external kernel name 'SetEndOfFile';
|
|
function SetFilePointer(Handle, Distance: Integer; DistanceHigh: Pointer;
|
|
MoveMethod: Integer): Integer; stdcall;
|
|
external kernel name 'SetFilePointer';
|
|
procedure UnhandledExceptionFilter; stdcall;
|
|
external kernel name 'UnhandledExceptionFilter';
|
|
function WriteFile(hFile: Integer; const Buffer; nNumberOfBytesToWrite: Cardinal;
|
|
var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Integer; stdcall;
|
|
external kernel name 'WriteFile';
|
|
|
|
function CharNext(lpsz: PChar): PChar; stdcall;
|
|
external user name 'CharNextA';
|
|
|
|
function CreateThread(SecurityAttributes: Pointer; StackSize: LongWord;
|
|
ThreadFunc: TThreadFunc; Parameter: Pointer;
|
|
CreationFlags: LongWord; var ThreadId: LongWord): Integer; stdcall;
|
|
external kernel name 'CreateThread';
|
|
|
|
procedure ExitThread(ExitCode: Integer); stdcall;
|
|
external kernel name 'ExitThread';
|
|
|
|
procedure ExitProcess(ExitCode: Integer); stdcall;
|
|
external kernel name 'ExitProcess';
|
|
|
|
procedure MessageBox(Wnd: Integer; Text: PChar; Caption: PChar; Typ: Integer); stdcall;
|
|
external user name 'MessageBoxA';
|
|
|
|
function CreateDirectory(PathName: PChar; Attr: Integer): WordBool; stdcall;
|
|
external kernel name 'CreateDirectoryA';
|
|
|
|
function FindClose(FindFile: Integer): LongBool; stdcall;
|
|
external kernel name 'FindClose';
|
|
|
|
function FindFirstFile(FileName: PChar; var FindFileData: TWIN32FindData): Integer; stdcall;
|
|
external kernel name 'FindFirstFileA';
|
|
|
|
{X} //function FreeLibrary(ModuleHandle: Longint): LongBool; stdcall;
|
|
{X} // external kernel name 'FreeLibrary';
|
|
|
|
{X} //function GetCommandLine: PChar; stdcall;
|
|
{X} // external kernel name 'GetCommandLineA';
|
|
|
|
function GetCurrentDirectory(BufSize: Integer; Buffer: PChar): Integer; stdcall;
|
|
external kernel name 'GetCurrentDirectoryA';
|
|
|
|
function GetLastError: Integer; stdcall;
|
|
external kernel name 'GetLastError';
|
|
|
|
procedure SetLastError(ErrorCode: Integer); stdcall;
|
|
external kernel name 'SetLastError';
|
|
|
|
function GetLocaleInfo(Locale: Longint; LCType: Longint; lpLCData: PChar; cchData: Integer): Integer; stdcall;
|
|
external kernel name 'GetLocaleInfoA';
|
|
|
|
{X} //function GetModuleFileName(Module: Integer; Filename: PChar;
|
|
{X} // Size: Integer): Integer; stdcall;
|
|
{X} // external kernel name 'GetModuleFileNameA';
|
|
|
|
{X} //function GetModuleHandle(ModuleName: PChar): Integer; stdcall;
|
|
{X} // external kernel name 'GetModuleHandleA';
|
|
|
|
function GetProcAddress(Module: Integer; ProcName: PChar): Pointer; stdcall;
|
|
external kernel name 'GetProcAddress';
|
|
|
|
procedure GetStartupInfo(var lpStartupInfo: TStartupInfo); stdcall;
|
|
external kernel name 'GetStartupInfoA';
|
|
|
|
function GetThreadLocale: Longint; stdcall;
|
|
external kernel name 'GetThreadLocale';
|
|
|
|
function LoadLibraryEx(LibName: PChar; hFile: Longint; Flags: Longint): Longint; stdcall;
|
|
external kernel name 'LoadLibraryExA';
|
|
|
|
function LoadString(Instance: Longint; IDent: Integer; Buffer: PChar;
|
|
Size: Integer): Integer; stdcall;
|
|
external user name 'LoadStringA';
|
|
|
|
{function lstrcat(lpString1, lpString2: PChar): PChar; stdcall;
|
|
external kernel name 'lstrcatA';}
|
|
|
|
function lstrcpy(lpString1, lpString2: PChar): PChar; stdcall;
|
|
external kernel name 'lstrcpyA';
|
|
|
|
function lstrcpyn(lpString1, lpString2: PChar;
|
|
iMaxLength: Integer): PChar; stdcall;
|
|
external kernel name 'lstrcpynA';
|
|
|
|
function _strlen(lpString: PChar): Integer; stdcall;
|
|
external kernel name 'lstrlenA';
|
|
|
|
function MultiByteToWideChar(CodePage, Flags: Integer; MBStr: PChar;
|
|
MBCount: Integer; WCStr: PWideChar; WCCount: Integer): Integer; stdcall;
|
|
external kernel name 'MultiByteToWideChar';
|
|
|
|
function RegCloseKey(hKey: Integer): Longint; stdcall;
|
|
external advapi32 name 'RegCloseKey';
|
|
|
|
function RegOpenKeyEx(hKey: LongWord; lpSubKey: PChar; ulOptions,
|
|
samDesired: LongWord; var phkResult: LongWord): Longint; stdcall;
|
|
external advapi32 name 'RegOpenKeyExA';
|
|
|
|
function RegQueryValueEx(hKey: LongWord; lpValueName: PChar;
|
|
lpReserved: Pointer; lpType: Pointer; lpData: PChar; lpcbData: Pointer): Integer; stdcall;
|
|
external advapi32 name 'RegQueryValueExA';
|
|
|
|
function RemoveDirectory(PathName: PChar): WordBool; stdcall;
|
|
external kernel name 'RemoveDirectoryA';
|
|
|
|
function SetCurrentDirectory(PathName: PChar): WordBool; stdcall;
|
|
external kernel name 'SetCurrentDirectoryA';
|
|
|
|
function WideCharToMultiByte(CodePage, Flags: Integer; WCStr: PWideChar;
|
|
WCCount: Integer; MBStr: PChar; MBCount: Integer; DefaultChar: PChar;
|
|
UsedDefaultChar: Pointer): Integer; stdcall;
|
|
external kernel name 'WideCharToMultiByte';
|
|
|
|
function VirtualQuery(lpAddress: Pointer;
|
|
var lpBuffer: TMemInfo; dwLength: Longint): Longint; stdcall;
|
|
external kernel name 'VirtualQuery';
|
|
|
|
//function SysAllocString(P: PWideChar): PWideChar; stdcall;
|
|
// external oleaut name 'SysAllocString';
|
|
|
|
function SysAllocStringLen(P: PWideChar; Len: Integer): PWideChar; stdcall;
|
|
external oleaut name 'SysAllocStringLen';
|
|
|
|
function SysReAllocStringLen(var S: WideString; P: PWideChar;
|
|
Len: Integer): LongBool; stdcall;
|
|
external oleaut name 'SysReAllocStringLen';
|
|
|
|
procedure SysFreeString(const S: WideString); stdcall;
|
|
external oleaut name 'SysFreeString';
|
|
|
|
function SysStringLen(const S: WideString): Integer; stdcall;
|
|
external oleaut name 'SysStringLen';
|
|
|
|
function InterlockedIncrement(var Addend: Integer): Integer; stdcall;
|
|
external kernel name 'InterlockedIncrement';
|
|
|
|
function InterlockedDecrement(var Addend: Integer): Integer; stdcall;
|
|
external kernel name 'InterlockedDecrement';
|
|
|
|
function GetCurrentThreadId: LongWord; stdcall;
|
|
external kernel name 'GetCurrentThreadId';
|
|
|
|
function GetVersion: LongWord; stdcall;
|
|
external kernel name 'GetVersion';
|
|
|
|
function QueryPerformanceCounter(var lpPerformanceCount: Int64): LongBool; stdcall;
|
|
external kernel name 'QueryPerformanceCounter';
|
|
|
|
function GetTickCount: Cardinal;
|
|
external kernel name 'GetTickCount';
|
|
|
|
function GetCmdShow: Integer;
|
|
var
|
|
SI: TStartupInfo;
|
|
begin
|
|
Result := 10; { SW_SHOWDEFAULT }
|
|
GetStartupInfo(SI);
|
|
if SI.dwFlags and 1 <> 0 then { STARTF_USESHOWWINDOW }
|
|
Result := SI.wShowWindow;
|
|
end;
|
|
|
|
{$ENDIF} // MSWindows
|
|
|
|
function WCharFromChar(WCharDest: PWideChar; DestChars: Integer; const CharSource: PChar; SrcBytes: Integer): Integer; forward;
|
|
function CharFromWChar(CharDest: PChar; DestBytes: Integer; const WCharSource: PWideChar; SrcChars: Integer): Integer; forward;
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ Memory manager }
|
|
{ ----------------------------------------------------- }
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
{$I GETMEM.INC }
|
|
{$ENDIF}
|
|
|
|
|
|
//////////////////// This code is from HeapMM.pas, (C) by Vladimir Kladov, 2001
|
|
const
|
|
HEAP_NO_SERIALIZE = $00001;
|
|
HEAP_GROWABLE = $00002;
|
|
HEAP_GENERATE_EXCEPTIONS = $00004;
|
|
HEAP_ZERO_MEMORY = $00008;
|
|
HEAP_REALLOC_IN_PLACE_ONLY = $00010;
|
|
HEAP_TAIL_CHECKING_ENABLED = $00020;
|
|
HEAP_FREE_CHECKING_ENABLED = $00040;
|
|
HEAP_DISABLE_COALESCE_ON_FREE = $00080;
|
|
HEAP_CREATE_ALIGN_16 = $10000;
|
|
HEAP_CREATE_ENABLE_TRACING = $20000;
|
|
HEAP_MAXIMUM_TAG = $00FFF;
|
|
HEAP_PSEUDO_TAG_FLAG = $08000;
|
|
HEAP_TAG_SHIFT = 16 ;
|
|
|
|
{$DEFINE USE_PROCESS_HEAP}
|
|
|
|
var
|
|
HeapHandle: THandle;
|
|
{* Global handle to the heap. Do not change it! }
|
|
|
|
HeapFlags: DWORD = 0;
|
|
{* Possible flags are:
|
|
HEAP_GENERATE_EXCEPTIONS - system will raise an exception to indicate a
|
|
function failure, such as an out-of-memory
|
|
condition, instead of returning NULL.
|
|
HEAP_NO_SERIALIZE - mutual exclusion will not be used while the HeapAlloc
|
|
function is accessing the heap. Be careful!
|
|
Not recommended for multi-thread applications.
|
|
But faster.
|
|
HEAP_ZERO_MEMORY - obviously. (Slower!)
|
|
}
|
|
|
|
{ Note from MSDN:
|
|
The granularity of heap allocations in Win32 is 16 bytes. So if you
|
|
request a global memory allocation of 1 byte, the heap returns a pointer
|
|
to a chunk of memory, guaranteeing that the 1 byte is available. Chances
|
|
are, 16 bytes will actually be available because the heap cannot allocate
|
|
less than 16 bytes at a time.
|
|
}
|
|
|
|
function DfltGetMem(size: Integer): Pointer;
|
|
// Allocate memory block.
|
|
begin
|
|
Result := HeapAlloc( HeapHandle, HeapFlags, size );
|
|
end;
|
|
|
|
function DfltFreeMem(p: Pointer): Integer;
|
|
// Deallocate memory block.
|
|
begin
|
|
Result := Integer( not HeapFree( HeapHandle, HeapFlags and HEAP_NO_SERIALIZE,
|
|
p ) );
|
|
end;
|
|
|
|
function DfltReallocMem(p: Pointer; size: Integer): Pointer;
|
|
// Resize memory block.
|
|
begin
|
|
Result := HeapRealloc( HeapHandle, HeapFlags and (HEAP_NO_SERIALIZE and
|
|
HEAP_GENERATE_EXCEPTIONS and HEAP_ZERO_MEMORY),
|
|
// (Prevent using flag HEAP_REALLOC_IN_PLACE_ONLY here - to allow
|
|
// system to move the block if necessary).
|
|
p, size );
|
|
end;
|
|
|
|
//////////////////////////////////////////// end of HeapMM
|
|
|
|
|
|
{$IFDEF LINUX}
|
|
function SysGetMem(Size: Integer): Pointer;
|
|
begin
|
|
Result := __malloc(size);
|
|
end;
|
|
|
|
function SysFreeMem(P: Pointer): Integer;
|
|
begin
|
|
__free(P);
|
|
Result := 0;
|
|
end;
|
|
|
|
function SysReallocMem(P: Pointer; Size: Integer): Pointer;
|
|
begin
|
|
Result := __realloc(P, Size);
|
|
end;
|
|
|
|
{$ENDIF}
|
|
|
|
{X- by default, system memory allocation routines (API calls)
|
|
are used. To use Inprise's memory manager (Delphi standard)
|
|
call UseDelphiMemoryManager procedure. }
|
|
var
|
|
MemoryManager: TMemoryManager = (
|
|
GetMem: DfltGetMem;
|
|
FreeMem: DfltFreeMem;
|
|
ReallocMem: DfltReallocMem);
|
|
|
|
const
|
|
DelphiMemoryManager: TMemoryManager = (
|
|
GetMem: SysGetMem;
|
|
FreeMem: SysFreeMem;
|
|
ReallocMem: SysReallocMem);
|
|
|
|
procedure UseDelphiMemoryManager;
|
|
begin
|
|
IsMemoryManagerSet := IsDelphiMemoryManagerSet;
|
|
SetMemoryManager( DelphiMemoryManager );
|
|
end;
|
|
{X+}
|
|
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
var
|
|
// Unwinder: TUnwinder = (
|
|
// RaiseException: UnwindRaiseException;
|
|
// RegisterIPLookup: UnwindRegisterIPLookup;
|
|
// UnregisterIPLookup: UnwindUnregisterIPLookup;
|
|
// DelphiLookup: UnwindDelphiLookup);
|
|
Unwinder: TUnwinder;
|
|
|
|
{$IFDEF STATIC_UNWIND}
|
|
{$IFDEF PIC}
|
|
{$L 'objs/arith.pic.o'}
|
|
{$L 'objs/diag.pic.o'}
|
|
{$L 'objs/delphiuw.pic.o'}
|
|
{$L 'objs/unwind.pic.o'}
|
|
{$ELSE}
|
|
{$L 'objs/arith.o'}
|
|
{$L 'objs/diag.o'}
|
|
{$L 'objs/delphiuw.o'}
|
|
{$L 'objs/unwind.o'}
|
|
{$ENDIF}
|
|
procedure Arith_RdUnsigned; external;
|
|
procedure Arith_RdSigned; external;
|
|
procedure __assert_fail; cdecl; external libc name '__assert_fail';
|
|
procedure malloc; cdecl; external libc name 'malloc';
|
|
procedure memset; cdecl; external libc name 'memset';
|
|
procedure strchr; cdecl; external libc name 'strchr';
|
|
procedure strncpy; cdecl; external libc name 'strncpy';
|
|
procedure strcpy; cdecl; external libc name 'strcpy';
|
|
procedure strcmp; cdecl; external libc name 'strcmp';
|
|
procedure printf; cdecl; external libc name 'printf';
|
|
procedure free; cdecl; external libc name 'free';
|
|
procedure getenv; cdecl; external libc name 'getenv';
|
|
procedure strtok; cdecl; external libc name 'strtok';
|
|
procedure strdup; cdecl; external libc name 'strdup';
|
|
procedure __strdup; cdecl; external libc name '__strdup';
|
|
procedure fopen; cdecl; external libc name 'fopen';
|
|
procedure fdopen; cdecl; external libc name 'fdopen';
|
|
procedure time; cdecl; external libc name 'time';
|
|
procedure ctime; cdecl; external libc name 'ctime';
|
|
procedure fclose; cdecl; external libc name 'fclose';
|
|
procedure fprintf; cdecl; external libc name 'fprintf';
|
|
procedure vfprintf; cdecl; external libc name 'vfprintf';
|
|
procedure fflush; cdecl; external libc name 'fflush';
|
|
procedure debug_init; external;
|
|
procedure debug_print; external;
|
|
procedure debug_class_enabled; external;
|
|
procedure debug_continue; external;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
{X}{$IFDEF MSWINDOWS}
|
|
{X}function _GetMem(Size: Integer): Pointer;
|
|
{X}asm
|
|
{X} TEST EAX,EAX
|
|
{X} JE @@1
|
|
{X} CALL MemoryManager.GetMem
|
|
{X} OR EAX,EAX
|
|
{X} JE @@2
|
|
{X}@@1: RET
|
|
{X}@@2: MOV AL,reOutOfMemory
|
|
{X} JMP Error
|
|
{X}end;
|
|
{X}{$ELSE}
|
|
function _GetMem(Size: Integer): Pointer;
|
|
{$IF Defined(DEBUG) and Defined(LINUX)}
|
|
var
|
|
Signature: PLongInt;
|
|
{$IFEND}
|
|
begin
|
|
if Size > 0 then
|
|
begin
|
|
{$IF Defined(DEBUG) and Defined(LINUX)}
|
|
Signature := PLongInt(MemoryManager.GetMem(Size + 4));
|
|
if Signature = nil then
|
|
Error(reOutOfMemory);
|
|
Signature^ := 0;
|
|
Result := Pointer(LongInt(Signature) + 4);
|
|
{$ELSE}
|
|
Result := MemoryManager.GetMem(Size);
|
|
if Result = nil then
|
|
Error(reOutOfMemory);
|
|
{$IFEND}
|
|
end
|
|
else
|
|
Result := nil;
|
|
end;
|
|
{X}{$ENDIF MSWINDOWS}
|
|
|
|
|
|
const
|
|
FreeMemorySignature = Longint($FBEEFBEE);
|
|
|
|
{X}{$IFDEF MSWINDOWS}
|
|
{X}function _FreeMem(P: Pointer): Integer;
|
|
{X}asm
|
|
{X} TEST EAX,EAX
|
|
{X} JE @@1
|
|
{X} CALL MemoryManager.FreeMem
|
|
{X} OR EAX,EAX
|
|
{X} JNE @@2
|
|
{X}@@1: RET
|
|
{X}@@2: MOV AL,reInvalidPtr
|
|
{X} JMP Error
|
|
{X}end;
|
|
{X}{$ELSE}
|
|
function _FreeMem(P: Pointer): Integer;
|
|
{$IF Defined(DEBUG) and Defined(LINUX)}
|
|
var
|
|
Signature: PLongInt;
|
|
{$IFEND}
|
|
begin
|
|
if P <> nil then
|
|
begin
|
|
{$IF Defined(DEBUG) and Defined(LINUX)}
|
|
Signature := PLongInt(LongInt(P) - 4);
|
|
if Signature^ <> 0 then
|
|
Error(reInvalidPtr);
|
|
Signature^ := FreeMemorySignature;
|
|
Result := MemoryManager.Freemem(Pointer(Signature));
|
|
{$ELSE}
|
|
Result := MemoryManager.FreeMem(P);
|
|
{$IFEND}
|
|
if Result <> 0 then
|
|
Error(reInvalidPtr);
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
{X}{$ENDIF MSWINDOWS}
|
|
|
|
{$IFDEF LINUX}
|
|
function _ReallocMem(var P: Pointer; NewSize: Integer): Pointer;
|
|
{$IFDEF DEBUG}
|
|
var
|
|
Temp: Pointer;
|
|
{$ENDIF}
|
|
begin
|
|
if P <> nil then
|
|
begin
|
|
{$IFDEF DEBUG}
|
|
Temp := Pointer(LongInt(P) - 4);
|
|
if NewSize > 0 then
|
|
begin
|
|
Temp := MemoryManager.ReallocMem(Temp, NewSize + 4);
|
|
Result := Pointer(LongInt(Temp) + 4);
|
|
end
|
|
else
|
|
begin
|
|
MemoryManager.FreeMem(Temp);
|
|
Result := nil;
|
|
end;
|
|
{$ELSE}
|
|
if NewSize > 0 then
|
|
begin
|
|
Result := MemoryManager.ReallocMem(P, NewSize);
|
|
end
|
|
else
|
|
begin
|
|
MemoryManager.FreeMem(P);
|
|
Result := nil;
|
|
end;
|
|
{$ENDIF}
|
|
P := Result;
|
|
end else
|
|
begin
|
|
Result := _GetMem(NewSize);
|
|
P := Result;
|
|
end;
|
|
end;
|
|
{$ELSEIF Defined(MSWINDOWS)}
|
|
function _ReallocMem(var P: Pointer; NewSize: Integer): Pointer;
|
|
asm
|
|
MOV ECX,[EAX]
|
|
TEST ECX,ECX
|
|
JE @@alloc
|
|
TEST EDX,EDX
|
|
JE @@free
|
|
@@resize:
|
|
PUSH EAX
|
|
MOV EAX,ECX
|
|
CALL MemoryManager.ReallocMem
|
|
POP ECX
|
|
OR EAX,EAX
|
|
JE @@allocError
|
|
MOV [ECX],EAX
|
|
RET
|
|
@@freeError:
|
|
MOV AL,reInvalidPtr
|
|
JMP Error
|
|
@@free:
|
|
MOV [EAX],EDX
|
|
MOV EAX,ECX
|
|
CALL MemoryManager.FreeMem
|
|
OR EAX,EAX
|
|
JNE @@freeError
|
|
RET
|
|
@@allocError:
|
|
MOV AL,reOutOfMemory
|
|
JMP Error
|
|
@@alloc:
|
|
TEST EDX,EDX
|
|
JE @@exit
|
|
PUSH EAX
|
|
MOV EAX,EDX
|
|
CALL MemoryManager.GetMem
|
|
POP ECX
|
|
OR EAX,EAX
|
|
JE @@allocError
|
|
MOV [ECX],EAX
|
|
@@exit:
|
|
end;
|
|
{$IFEND}
|
|
|
|
procedure GetMemoryManager(var MemMgr: TMemoryManager);
|
|
begin
|
|
MemMgr := MemoryManager;
|
|
end;
|
|
|
|
procedure SetMemoryManager(const MemMgr: TMemoryManager);
|
|
begin
|
|
MemoryManager := MemMgr;
|
|
end;
|
|
|
|
//{X} - function is replaced with pointer to one.
|
|
// function IsMemoryManagerSet: Boolean;
|
|
function IsDelphiMemoryManagerSet: Boolean;
|
|
begin
|
|
with MemoryManager do
|
|
Result := (@GetMem <> @SysGetMem) or (@FreeMem <> @SysFreeMem) or
|
|
(@ReallocMem <> @SysReallocMem);
|
|
end;
|
|
|
|
{X+ always returns False. Initial handler for IsMemoryManagerSet }
|
|
function MemoryManagerNotUsed : Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
{X-}
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure GetUnwinder(var Dest: TUnwinder);
|
|
begin
|
|
Dest := Unwinder;
|
|
end;
|
|
|
|
procedure SetUnwinder(const NewUnwinder: TUnwinder);
|
|
begin
|
|
Unwinder := NewUnwinder;
|
|
end;
|
|
|
|
function IsUnwinderSet: Boolean;
|
|
begin
|
|
with Unwinder do
|
|
Result := (@RaiseException <> @_BorUnwind_RaiseException) or
|
|
(@RegisterIPLookup <> @_BorUnwind_RegisterIPLookup) or
|
|
(@UnregisterIPLookup <> @_BorUnwind_UnregisterIPLookup) or
|
|
(@DelphiLookup <> @_BorUnwind_DelphiLookup);
|
|
end;
|
|
|
|
procedure InitUnwinder;
|
|
var
|
|
Addr: Pointer;
|
|
begin
|
|
{
|
|
We look to see if we can find a dynamic version of the unwinder. This will
|
|
be the case if the application used Unwind.pas. If it is present, then we
|
|
fire it up. Otherwise, we use our static copy.
|
|
}
|
|
Addr := dlsym(0, '_BorUnwind_RegisterIPLookup');
|
|
if Addr <> nil then
|
|
begin
|
|
Unwinder.RegisterIPLookup := Addr;
|
|
Addr := dlsym(0, '_BorUnwind_UnregisterIPLookup');
|
|
Unwinder.UnregisterIPLookup := Addr;
|
|
Addr := dlsym(0, '_BorUnwind_RaiseException');
|
|
Unwinder.RaiseException := Addr;
|
|
Addr := dlsym(0, '_BorUnwind_DelphiLookup');
|
|
Unwinder.DelphiLookup := Addr;
|
|
Addr := dlsym(0, '_BorUnwind_ClosestHandler');
|
|
Unwinder.ClosestHandler := Addr;
|
|
end
|
|
else
|
|
begin
|
|
dlerror; // clear error state; dlsym doesn't
|
|
Unwinder.RegisterIPLookup := _BorUnwind_RegisterIPLookup;
|
|
Unwinder.DelphiLookup := _BorUnwind_DelphiLookup;
|
|
Unwinder.UnregisterIPLookup := _BorUnwind_UnregisterIPLookup;
|
|
Unwinder.RaiseException := _BorUnwind_RaiseException;
|
|
Unwinder.ClosestHandler := _BorUnwind_ClosestDelphiHandler;
|
|
end;
|
|
end;
|
|
|
|
function SysClosestDelphiHandler(Context: Pointer): LongWord;
|
|
begin
|
|
if not Assigned(Unwinder.ClosestHandler) then
|
|
InitUnwinder;
|
|
Result := Unwinder.ClosestHandler(Context);
|
|
end;
|
|
|
|
function SysRegisterIPLookup(StartAddr, EndAddr: LongInt; Context: Pointer; GOT: LongInt): LongBool;
|
|
begin
|
|
// xxx
|
|
if not Assigned(Unwinder.RegisterIPLookup) then
|
|
begin
|
|
InitUnwinder;
|
|
// Unwinder.RegisterIPLookup := UnwindRegisterIPLookup;
|
|
// Unwinder.DelphiLookup := UnwindDelphiLookup;
|
|
end;
|
|
Result := Unwinder.RegisterIPLookup(@Unwinder.DelphiLookup, StartAddr, EndAddr, Context, GOT);
|
|
end;
|
|
|
|
procedure SysUnregisterIPLookup(StartAddr: LongInt);
|
|
begin
|
|
// if not Assigned(Unwinder.UnregisterIPLookup) then
|
|
// Unwinder.UnregisterIPLookup := UnwindUnregisterIPLookup;
|
|
Unwinder.UnregisterIPLookup(StartAddr);
|
|
end;
|
|
|
|
function SysRaiseException(Exc: Pointer): LongBool; export;
|
|
begin
|
|
// if not Assigned(Unwinder.RaiseException) then
|
|
// Unwinder.RaiseException := UnwindRaiseException;
|
|
Result := Unwinder.RaiseException(Exc);
|
|
end;
|
|
|
|
const
|
|
MAX_NESTED_EXCEPTIONS = 16;
|
|
{$ENDIF}
|
|
|
|
threadvar
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
ExceptionObjects: array[0..MAX_NESTED_EXCEPTIONS-1] of TRaisedException;
|
|
ExceptionObjectCount: Integer;
|
|
OSExceptionsBlocked: Integer;
|
|
{$ELSE}
|
|
RaiseListPtr: pointer;
|
|
{$ENDIF}
|
|
InOutRes: Integer;
|
|
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
notimpl: array [0..15] of Char = 'not implemented'#10;
|
|
|
|
procedure NotImplemented;
|
|
begin
|
|
__write (2, @notimpl, 16);
|
|
Halt;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure BlockOSExceptions;
|
|
asm
|
|
PUSH EAX
|
|
PUSH EDX
|
|
CALL SysInit.@GetTLS
|
|
MOV [EAX].OSExceptionsBlocked, 1
|
|
POP EDX
|
|
POP EAX
|
|
end;
|
|
|
|
procedure UnblockOSExceptions;
|
|
asm
|
|
PUSH EAX
|
|
CALL SysInit.@GetTLS
|
|
MOV [EAX].OSExceptionsBlocked, 0
|
|
POP EAX
|
|
end;
|
|
|
|
function AreOSExceptionsBlocked: Boolean;
|
|
asm
|
|
CALL SysInit.@GetTLS
|
|
MOV EAX, [EAX].OSExceptionsBlocked
|
|
end;
|
|
|
|
const
|
|
TRAISEDEXCEPTION_SIZE = SizeOf(TRaisedException);
|
|
|
|
function CurrentException: PRaisedException;
|
|
asm
|
|
CALL SysInit.@GetTLS
|
|
LEA EDX, [EAX].ExceptionObjects
|
|
MOV EAX, [EAX].ExceptionObjectCount
|
|
OR EAX, EAX
|
|
JE @@Done
|
|
DEC EAX
|
|
IMUL EAX, TRAISEDEXCEPTION_SIZE
|
|
ADD EAX, EDX
|
|
@@Done:
|
|
end;
|
|
|
|
function AllocateException(Exception: Pointer; ExceptionAddr: Pointer): PRaisedException;
|
|
asm
|
|
PUSH EAX
|
|
PUSH EDX
|
|
CALL SysInit.@GetTLS
|
|
CMP [EAX].ExceptionObjectCount, MAX_NESTED_EXCEPTIONS-1
|
|
JE @@TooManyNestedExceptions
|
|
INC [EAX].ExceptionObjectCount
|
|
CALL CurrentException
|
|
POP EDX
|
|
POP ECX
|
|
MOV [EAX].TRaisedException.ExceptObject, ECX
|
|
MOV [EAX].TRaisedException.ExceptionAddr, EDX
|
|
MOV [EAX].TRaisedException.RefCount, 0
|
|
MOV [EAX].TRaisedException.HandlerEBP, $FFFFFFFF
|
|
MOV [EAX].TRaisedException.Flags, 0
|
|
RET
|
|
@@TooManyNestedExceptions:
|
|
MOV EAX, 231
|
|
JMP _RunError
|
|
end;
|
|
|
|
{
|
|
In the interests of code size here, this function is slightly overloaded.
|
|
It is responsible for freeing up the current exception record on the
|
|
exception stack, and it conditionally returns the thrown object to the
|
|
caller. If the object has been acquired through AcquireExceptionObject,
|
|
we don't return the thrown object.
|
|
}
|
|
function FreeException: Pointer;
|
|
asm
|
|
CALL CurrentException
|
|
OR EAX, EAX
|
|
JE @@Error
|
|
{ EAX -> the TRaisedException }
|
|
XOR ECX, ECX
|
|
{ If the exception object has been referenced, we don't return it. }
|
|
CMP [EAX].TRaisedException.RefCount, 0
|
|
JA @@GotObject
|
|
MOV ECX, [EAX].TRaisedException.ExceptObject
|
|
@@GotObject:
|
|
PUSH ECX
|
|
CALL SysInit.@GetTLS
|
|
POP ECX
|
|
DEC [EAX].ExceptionObjectCount
|
|
MOV EAX, ECX
|
|
RET
|
|
@@Error:
|
|
{ Some kind of internal error }
|
|
JMP _Run0Error
|
|
end;
|
|
|
|
function AcquireExceptionObject: Pointer;
|
|
asm
|
|
CALL CurrentException
|
|
OR EAX, EAX
|
|
JE @@Error
|
|
INC [EAX].TRaisedException.RefCount
|
|
MOV EAX, [EAX].TRaisedException.ExceptObject
|
|
RET
|
|
@@Error:
|
|
{ This happens if there is no exception pending }
|
|
JMP _Run0Error
|
|
end;
|
|
|
|
procedure ReleaseExceptionObject;
|
|
asm
|
|
CALL CurrentException
|
|
OR EAX, EAX
|
|
JE @@Error
|
|
CMP [EAX].TRaisedException.RefCount, 0
|
|
JE @@Error
|
|
DEC [EAX].TRaisedException.RefCount
|
|
RET
|
|
@@Error:
|
|
|
|
{
|
|
This happens if there is no exception pending, or
|
|
if the reference count on a pending exception is
|
|
zero.
|
|
}
|
|
JMP _Run0Error
|
|
end;
|
|
|
|
function ExceptObject: TObject;
|
|
var
|
|
Exc: PRaisedException;
|
|
begin
|
|
Exc := CurrentException;
|
|
if Exc <> nil then
|
|
Result := TObject(Exc^.ExceptObject)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
{ Return current exception address }
|
|
|
|
function ExceptAddr: Pointer;
|
|
var
|
|
Exc: PRaisedException;
|
|
begin
|
|
Exc := CurrentException;
|
|
if Exc <> nil then
|
|
Result := Exc^.ExceptionAddr
|
|
else
|
|
Result := nil;
|
|
end;
|
|
{$ELSE} {not PC_MAPPED_EXCEPTIONS}
|
|
|
|
function ExceptObject: TObject;
|
|
begin
|
|
if RaiseListPtr <> nil then
|
|
Result := PRaiseFrame(RaiseListPtr)^.ExceptObject
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
{ Return current exception address }
|
|
|
|
function ExceptAddr: Pointer;
|
|
begin
|
|
if RaiseListPtr <> nil then
|
|
Result := PRaiseFrame(RaiseListPtr)^.ExceptAddr
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
|
|
function AcquireExceptionObject: Pointer;
|
|
begin
|
|
if RaiseListPtr <> nil then
|
|
begin
|
|
Result := PRaiseFrame(RaiseListPtr)^.ExceptObject;
|
|
PRaiseFrame(RaiseListPtr)^.ExceptObject := nil;
|
|
end
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure ReleaseExceptionObject;
|
|
begin
|
|
end;
|
|
|
|
function RaiseList: Pointer;
|
|
begin
|
|
Result := RaiseListPtr;
|
|
end;
|
|
|
|
function SetRaiseList(NewPtr: Pointer): Pointer;
|
|
asm
|
|
PUSH EAX
|
|
CALL SysInit.@GetTLS
|
|
MOV EDX, [EAX].RaiseListPtr
|
|
POP [EAX].RaiseListPtr
|
|
MOV EAX, EDX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ local functions & procedures of the system unit }
|
|
{ ----------------------------------------------------- }
|
|
|
|
procedure RunErrorAt(ErrCode: Integer; ErrorAtAddr: Pointer);
|
|
begin
|
|
ErrorAddr := ErrorAtAddr;
|
|
_Halt(ErrCode);
|
|
end;
|
|
|
|
procedure ErrorAt(ErrorCode: Byte; ErrorAddr: Pointer);
|
|
|
|
const
|
|
reMap: array [TRunTimeError] of Byte = (
|
|
0,
|
|
203, { reOutOfMemory }
|
|
204, { reInvalidPtr }
|
|
200, { reDivByZero }
|
|
201, { reRangeError }
|
|
{ 210 abstract error }
|
|
215, { reIntOverflow }
|
|
207, { reInvalidOp }
|
|
200, { reZeroDivide }
|
|
205, { reOverflow }
|
|
206, { reUnderflow }
|
|
219, { reInvalidCast }
|
|
216, { Access violation }
|
|
202, { Stack overflow }
|
|
217, { Control-C }
|
|
218, { Privileged instruction }
|
|
220, { Invalid variant type cast }
|
|
221, { Invalid variant operation }
|
|
222, { No variant method call dispatcher }
|
|
223, { Cannot create variant array }
|
|
224, { Variant does not contain an array }
|
|
225, { Variant array bounds error }
|
|
{ 226 thread init failure }
|
|
227, { reAssertionFailed }
|
|
0, { reExternalException not used here; in SysUtils }
|
|
228, { reIntfCastError }
|
|
229 { reSafeCallError }
|
|
{ 230 Reserved by the compiler for unhandled exceptions }
|
|
{ 231 Too many nested exceptions }
|
|
{ 232 Fatal signal raised on a non-Delphi thread });
|
|
|
|
begin
|
|
errorCode := errorCode and 127;
|
|
if Assigned(ErrorProc) then
|
|
ErrorProc(errorCode, ErrorAddr);
|
|
if errorCode = 0 then
|
|
errorCode := InOutRes
|
|
else if errorCode <= Byte(High(TRuntimeError)) then
|
|
errorCode := reMap[TRunTimeError(errorCode)];
|
|
RunErrorAt(errorCode, ErrorAddr);
|
|
end;
|
|
|
|
procedure Error(errorCode: TRuntimeError);
|
|
asm
|
|
AND EAX,127
|
|
MOV EDX,[ESP]
|
|
JMP ErrorAt
|
|
end;
|
|
|
|
procedure __IOTest;
|
|
asm
|
|
PUSH EAX
|
|
PUSH EDX
|
|
PUSH ECX
|
|
CALL SysInit.@GetTLS
|
|
CMP [EAX].InOutRes,0
|
|
POP ECX
|
|
POP EDX
|
|
POP EAX
|
|
JNE @error
|
|
RET
|
|
@error:
|
|
XOR EAX,EAX
|
|
JMP Error
|
|
end;
|
|
|
|
procedure SetInOutRes(NewValue: Integer);
|
|
begin
|
|
InOutRes := NewValue;
|
|
end;
|
|
|
|
procedure InOutError;
|
|
begin
|
|
SetInOutRes(GetLastError);
|
|
end;
|
|
|
|
procedure ChDir(const S: string);
|
|
begin
|
|
ChDir(PChar(S));
|
|
end;
|
|
|
|
procedure ChDir(P: PChar);
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
if not SetCurrentDirectory(P) then
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
if __chdir(P) <> 0 then
|
|
{$ENDIF}
|
|
InOutError;
|
|
end;
|
|
|
|
procedure _Copy{ s : ShortString; index, count : Integer ) : ShortString};
|
|
asm
|
|
{ ->EAX Source string }
|
|
{ EDX index }
|
|
{ ECX count }
|
|
{ [ESP+4] Pointer to result string }
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
MOV EDI,[ESP+8+4]
|
|
|
|
XOR EAX,EAX
|
|
OR AL,[ESI]
|
|
JZ @@srcEmpty
|
|
|
|
{ limit index to satisfy 1 <= index <= Length(src) }
|
|
|
|
TEST EDX,EDX
|
|
JLE @@smallInx
|
|
CMP EDX,EAX
|
|
JG @@bigInx
|
|
@@cont1:
|
|
|
|
{ limit count to satisfy 0 <= count <= Length(src) - index + 1 }
|
|
|
|
SUB EAX,EDX { calculate Length(src) - index + 1 }
|
|
INC EAX
|
|
TEST ECX,ECX
|
|
JL @@smallCount
|
|
CMP ECX,EAX
|
|
JG @@bigCount
|
|
@@cont2:
|
|
|
|
ADD ESI,EDX
|
|
|
|
MOV [EDI],CL
|
|
INC EDI
|
|
REP MOVSB
|
|
JMP @@exit
|
|
|
|
@@smallInx:
|
|
MOV EDX,1
|
|
JMP @@cont1
|
|
@@bigInx:
|
|
{ MOV EDX,EAX
|
|
JMP @@cont1 }
|
|
@@smallCount:
|
|
XOR ECX,ECX
|
|
JMP @@cont2
|
|
@@bigCount:
|
|
MOV ECX,EAX
|
|
JMP @@cont2
|
|
@@srcEmpty:
|
|
MOV [EDI],AL
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
RET 4
|
|
end;
|
|
|
|
procedure _Delete{ var s : openstring; index, count : Integer };
|
|
asm
|
|
{ ->EAX Pointer to s }
|
|
{ EDX index }
|
|
{ ECX count }
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV EDI,EAX
|
|
|
|
XOR EAX,EAX
|
|
MOV AL,[EDI]
|
|
|
|
{ if index not in [1 .. Length(s)] do nothing }
|
|
|
|
TEST EDX,EDX
|
|
JLE @@exit
|
|
CMP EDX,EAX
|
|
JG @@exit
|
|
|
|
{ limit count to [0 .. Length(s) - index + 1] }
|
|
|
|
TEST ECX,ECX
|
|
JLE @@exit
|
|
SUB EAX,EDX { calculate Length(s) - index + 1 }
|
|
INC EAX
|
|
CMP ECX,EAX
|
|
JLE @@1
|
|
MOV ECX,EAX
|
|
@@1:
|
|
SUB [EDI],CL { reduce Length(s) by count }
|
|
ADD EDI,EDX { point EDI to first char to be deleted }
|
|
LEA ESI,[EDI+ECX] { point ESI to first char to be preserved }
|
|
SUB EAX,ECX { #chars = Length(s) - index + 1 - count }
|
|
MOV ECX,EAX
|
|
|
|
REP MOVSB
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
procedure _LGetDir(D: Byte; var S: string);
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Drive: array[0..3] of Char;
|
|
DirBuf, SaveBuf: array[0..MAX_PATH] of Char;
|
|
begin
|
|
if D <> 0 then
|
|
begin
|
|
Drive[0] := Chr(D + Ord('A') - 1);
|
|
Drive[1] := ':';
|
|
Drive[2] := #0;
|
|
GetCurrentDirectory(SizeOf(SaveBuf), SaveBuf);
|
|
SetCurrentDirectory(Drive);
|
|
end;
|
|
GetCurrentDirectory(SizeOf(DirBuf), DirBuf);
|
|
if D <> 0 then SetCurrentDirectory(SaveBuf);
|
|
S := DirBuf;
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
var
|
|
DirBuf: array[0..MAX_PATH] of Char;
|
|
begin
|
|
__getcwd(DirBuf, sizeof(DirBuf));
|
|
S := string(DirBuf);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _SGetDir(D: Byte; var S: ShortString);
|
|
var
|
|
L: string;
|
|
begin
|
|
_LGetDir(D, L);
|
|
S := L;
|
|
end;
|
|
|
|
procedure _Insert{ source : ShortString; var s : openstring; index : Integer };
|
|
asm
|
|
{ ->EAX Pointer to source string }
|
|
{ EDX Pointer to destination string }
|
|
{ ECX Length of destination string }
|
|
{ [ESP+4] Index }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH ECX
|
|
MOV ECX,[ESP+16+4]
|
|
SUB ESP,512 { VAR buf: ARRAY [0..511] of Char }
|
|
|
|
MOV EBX,EDX { save pointer to s for later }
|
|
MOV ESI,EDX
|
|
|
|
XOR EDX,EDX
|
|
MOV DL,[ESI]
|
|
INC ESI
|
|
|
|
{ limit index to [1 .. Length(s)+1] }
|
|
|
|
INC EDX
|
|
TEST ECX,ECX
|
|
JLE @@smallInx
|
|
CMP ECX,EDX
|
|
JG @@bigInx
|
|
@@cont1:
|
|
DEC EDX { EDX = Length(s) }
|
|
{ EAX = Pointer to src }
|
|
{ ESI = EBX = Pointer to s }
|
|
{ ECX = Index }
|
|
|
|
{ copy index-1 chars from s to buf }
|
|
|
|
MOV EDI,ESP
|
|
DEC ECX
|
|
SUB EDX,ECX { EDX = remaining length of s }
|
|
REP MOVSB
|
|
|
|
{ copy Length(src) chars from src to buf }
|
|
|
|
XCHG EAX,ESI { save pointer into s, point ESI to src }
|
|
MOV CL,[ESI] { ECX = Length(src) (ECX was zero after rep) }
|
|
INC ESI
|
|
REP MOVSB
|
|
|
|
{ copy remaining chars of s to buf }
|
|
|
|
MOV ESI,EAX { restore pointer into s }
|
|
MOV ECX,EDX { copy remaining bytes of s }
|
|
REP MOVSB
|
|
|
|
{ calculate total chars in buf }
|
|
|
|
SUB EDI,ESP { length = bufPtr - buf }
|
|
MOV ECX,[ESP+512] { ECX = Min(length, destLength) }
|
|
{ MOV ECX,[EBP-16] }{ ECX = Min(length, destLength) }
|
|
CMP ECX,EDI
|
|
JB @@1
|
|
MOV ECX,EDI
|
|
@@1:
|
|
MOV EDI,EBX { Point EDI to s }
|
|
MOV ESI,ESP { Point ESI to buf }
|
|
MOV [EDI],CL { Store length in s }
|
|
INC EDI
|
|
REP MOVSB { Copy length chars to s }
|
|
JMP @@exit
|
|
|
|
@@smallInx:
|
|
MOV ECX,1
|
|
JMP @@cont1
|
|
@@bigInx:
|
|
MOV ECX,EDX
|
|
JMP @@cont1
|
|
|
|
@@exit:
|
|
ADD ESP,512+4
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
RET 4
|
|
end;
|
|
|
|
function IOResult: Integer;
|
|
begin
|
|
Result := InOutRes;
|
|
InOutRes := 0;
|
|
end;
|
|
|
|
procedure MkDir(const S: string);
|
|
begin
|
|
MkDir(PChar(s));
|
|
end;
|
|
|
|
procedure MkDir(P: PChar);
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
if not CreateDirectory(P, 0) then
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
if __mkdir(P, -1) <> 0 then
|
|
{$ENDIF}
|
|
InOutError;
|
|
end;
|
|
|
|
procedure Move( const Source; var Dest; count : Integer );
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
S, D: PChar;
|
|
I: Integer;
|
|
begin
|
|
S := PChar(@Source);
|
|
D := PChar(@Dest);
|
|
if S = D then Exit;
|
|
if Cardinal(D) > Cardinal(S) then
|
|
for I := count-1 downto 0 do
|
|
D[I] := S[I]
|
|
else
|
|
for I := 0 to count-1 do
|
|
D[I] := S[I];
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ ->EAX Pointer to source }
|
|
{ EDX Pointer to destination }
|
|
{ ECX Count }
|
|
|
|
(*{X-} // original code.
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
|
|
MOV EAX,ECX
|
|
|
|
CMP EDI,ESI
|
|
JA @@down
|
|
JE @@exit
|
|
|
|
SAR ECX,2 { copy count DIV 4 dwords }
|
|
JS @@exit
|
|
|
|
REP MOVSD
|
|
|
|
MOV ECX,EAX
|
|
AND ECX,03H
|
|
REP MOVSB { copy count MOD 4 bytes }
|
|
JMP @@exit
|
|
|
|
@@down:
|
|
LEA ESI,[ESI+ECX-4] { point ESI to last dword of source }
|
|
LEA EDI,[EDI+ECX-4] { point EDI to last dword of dest }
|
|
|
|
SAR ECX,2 { copy count DIV 4 dwords }
|
|
JS @@exit
|
|
STD
|
|
REP MOVSD
|
|
|
|
MOV ECX,EAX
|
|
AND ECX,03H { copy count MOD 4 bytes }
|
|
ADD ESI,4-1 { point to last byte of rest }
|
|
ADD EDI,4-1
|
|
REP MOVSB
|
|
CLD
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
*){X+}
|
|
//---------------------------------------
|
|
(* {X+} // Let us write smaller:
|
|
JCXZ @@fin
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
|
|
MOV EAX,ECX
|
|
|
|
AND ECX,3 { copy count mod 4 dwords }
|
|
|
|
CMP EDI,ESI
|
|
JE @@exit
|
|
JA @@up
|
|
|
|
//down:
|
|
LEA ESI,[ESI+EAX-1] { point ESI to last byte of source }
|
|
LEA EDI,[EDI+EAX-1] { point EDI to last byte of dest }
|
|
STD
|
|
|
|
CMP EAX, 4
|
|
JL @@up
|
|
ADD ECX, 3 { move 3 bytes more to correct pos }
|
|
|
|
@@up:
|
|
REP MOVSB
|
|
|
|
SAR EAX, 2
|
|
JS @@exit
|
|
|
|
MOV ECX, EAX
|
|
REP MOVSD
|
|
|
|
@@exit:
|
|
CLD
|
|
POP EDI
|
|
POP ESI
|
|
|
|
@@fin:
|
|
*) {X-}
|
|
//---------------------------------------
|
|
{X+} // And now, let us write speedy:
|
|
CMP ECX, 4
|
|
JGE @@long
|
|
JCXZ @@fin
|
|
|
|
CMP EAX, EDX
|
|
JE @@fin
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV ESI, EAX
|
|
MOV EDI, EDX
|
|
JA @@short_up
|
|
|
|
LEA ESI,[ESI+ECX-1] { point ESI to last byte of source }
|
|
LEA EDI,[EDI+ECX-1] { point EDI to last byte of dest }
|
|
STD
|
|
|
|
@@short_up:
|
|
REP MOVSB
|
|
JMP @@exit_up
|
|
|
|
@@long:
|
|
CMP EAX, EDX
|
|
JE @@fin
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV ESI, EAX
|
|
MOV EDI, EDX
|
|
MOV EAX, ECX
|
|
|
|
JA @@long_up
|
|
|
|
{
|
|
SAR ECX, 2
|
|
JS @@exit
|
|
|
|
LEA ESI,[ESI+EAX-4]
|
|
LEA EDI,[EDI+EAX-4]
|
|
STD
|
|
REP MOVSD
|
|
|
|
MOV ECX, EAX
|
|
MOV EAX, 3
|
|
AND ECX, EAX
|
|
ADD ESI, EAX
|
|
ADD EDI, EAX
|
|
REP MOVSB
|
|
} // let's do it in other order - faster if data are aligned...
|
|
|
|
AND ECX, 3
|
|
LEA ESI,[ESI+EAX-1]
|
|
LEA EDI,[EDI+EAX-1]
|
|
STD
|
|
REP MOVSB
|
|
|
|
SAR EAX, 2
|
|
//JS @@exit // why to test this? but what does PC do?
|
|
MOV ECX, EAX
|
|
MOV EAX, 3
|
|
SUB ESI, EAX
|
|
SUB EDI, EAX
|
|
REP MOVSD
|
|
|
|
@@exit_up:
|
|
CLD
|
|
//JMP @@exit
|
|
DEC ECX // the same - loosing 2 tacts... but conveyer!
|
|
|
|
@@long_up:
|
|
SAR ECX, 2
|
|
JS @@exit
|
|
|
|
REP MOVSD
|
|
|
|
AND EAX, 3
|
|
MOV ECX, EAX
|
|
REP MOVSB
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
|
|
@@fin:
|
|
{X-}
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
function GetParamStr(P: PChar; var Param: string): PChar;
|
|
var
|
|
i, Len: Integer;
|
|
Start, S, Q: PChar;
|
|
begin
|
|
while True do
|
|
begin
|
|
while (P[0] <> #0) and (P[0] <= ' ') do
|
|
P := CharNext(P);
|
|
if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break;
|
|
end;
|
|
Len := 0;
|
|
Start := P;
|
|
while P[0] > ' ' do
|
|
begin
|
|
if P[0] = '"' then
|
|
begin
|
|
P := CharNext(P);
|
|
while (P[0] <> #0) and (P[0] <> '"') do
|
|
begin
|
|
Q := CharNext(P);
|
|
Inc(Len, Q - P);
|
|
P := Q;
|
|
end;
|
|
if P[0] <> #0 then
|
|
P := CharNext(P);
|
|
end
|
|
else
|
|
begin
|
|
Q := CharNext(P);
|
|
Inc(Len, Q - P);
|
|
P := Q;
|
|
end;
|
|
end;
|
|
|
|
SetLength(Param, Len);
|
|
|
|
P := Start;
|
|
S := Pointer(Param);
|
|
i := 0;
|
|
while P[0] > ' ' do
|
|
begin
|
|
if P[0] = '"' then
|
|
begin
|
|
P := CharNext(P);
|
|
while (P[0] <> #0) and (P[0] <> '"') do
|
|
begin
|
|
Q := CharNext(P);
|
|
while P < Q do
|
|
begin
|
|
S[i] := P^;
|
|
Inc(P);
|
|
Inc(i);
|
|
end;
|
|
end;
|
|
if P[0] <> #0 then P := CharNext(P);
|
|
end
|
|
else
|
|
begin
|
|
Q := CharNext(P);
|
|
while P < Q do
|
|
begin
|
|
S[i] := P^;
|
|
Inc(P);
|
|
Inc(i);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Result := P;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function ParamCount: Integer;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
P: PChar;
|
|
S: string;
|
|
begin
|
|
Result := 0;
|
|
P := GetParamStr(GetCommandLine, S);
|
|
while True do
|
|
begin
|
|
P := GetParamStr(P, S);
|
|
if S = '' then Break;
|
|
Inc(Result);
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
begin
|
|
if ArgCount > 1 then
|
|
Result := ArgCount - 1
|
|
else Result := 0;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
type
|
|
PCharArray = array[0..0] of PChar;
|
|
|
|
function ParamStr(Index: Integer): string;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
P: PChar;
|
|
Buffer: array[0..260] of Char;
|
|
begin
|
|
Result := '';
|
|
if Index = 0 then
|
|
SetString(Result, Buffer, GetModuleFileName(0, Buffer, SizeOf(Buffer)))
|
|
else
|
|
begin
|
|
P := GetCommandLine;
|
|
while True do
|
|
begin
|
|
P := GetParamStr(P, Result);
|
|
if (Index = 0) or (Result = '') then Break;
|
|
Dec(Index);
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
begin
|
|
if Index < ArgCount then
|
|
Result := PCharArray(ArgValues^)[Index]
|
|
else
|
|
Result := '';
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function Pos(const substr, str: AnsiString): Integer; overload;
|
|
asm
|
|
push ebx
|
|
push esi
|
|
add esp, -16
|
|
test edx, edx
|
|
jz @NotFound
|
|
test eax, eax
|
|
jz @NotFound
|
|
mov esi, [edx-4] //Length(Str)
|
|
mov ebx, [eax-4] //Length(Substr)
|
|
cmp esi, ebx
|
|
jl @NotFound
|
|
test ebx, ebx
|
|
jle @NotFound
|
|
dec ebx
|
|
add esi, edx
|
|
add edx, ebx
|
|
mov [esp+8], esi
|
|
add eax, ebx
|
|
mov [esp+4], edx
|
|
neg ebx
|
|
movzx ecx, byte ptr [eax]
|
|
mov [esp], ebx
|
|
jnz @FindString
|
|
|
|
sub esi, 2
|
|
mov [esp+12], esi
|
|
|
|
@FindChar2:
|
|
cmp cl, [edx]
|
|
jz @Matched0ch
|
|
cmp cl, [edx+1]
|
|
jz @Matched1ch
|
|
add edx, 2
|
|
cmp edx, [esp+12]
|
|
jb @FindChar4
|
|
cmp edx, [esp+8]
|
|
jb @FindChar2
|
|
@NotFound:
|
|
xor eax, eax
|
|
jmp @Exit0ch
|
|
|
|
@FindChar4:
|
|
cmp cl, [edx]
|
|
jz @Matched0ch
|
|
cmp cl, [edx+1]
|
|
jz @Matched1ch
|
|
cmp cl, [edx+2]
|
|
jz @Matched2ch
|
|
cmp cl, [edx+3]
|
|
jz @Matched3ch
|
|
add edx, 4
|
|
cmp edx, [esp+12]
|
|
jb @FindChar4
|
|
cmp edx, [esp+8]
|
|
jb @FindChar2
|
|
xor eax, eax
|
|
jmp @Exit0ch
|
|
|
|
@Matched2ch:
|
|
add edx, 2
|
|
@Matched0ch:
|
|
inc edx
|
|
mov eax, edx
|
|
sub eax, [esp+4]
|
|
@Exit0ch:
|
|
add esp, 16
|
|
pop esi
|
|
pop ebx
|
|
ret
|
|
|
|
@Matched3ch:
|
|
add edx, 2
|
|
@Matched1ch:
|
|
add edx, 2
|
|
xor eax, eax
|
|
cmp edx, [esp+8]
|
|
ja @Exit1ch
|
|
mov eax, edx
|
|
sub eax, [esp+4]
|
|
@Exit1ch:
|
|
add esp, 16
|
|
pop esi
|
|
pop ebx
|
|
ret
|
|
|
|
@FindString4:
|
|
cmp cl, [edx]
|
|
jz @Test0
|
|
cmp cl, [edx+1]
|
|
jz @Test1
|
|
cmp cl, [edx+2]
|
|
jz @Test2
|
|
cmp cl, [edx+3]
|
|
jz @Test3
|
|
add edx, 4
|
|
cmp edx, [esp+12]
|
|
jb @FindString4
|
|
cmp edx, [esp+8]
|
|
jb @FindString2
|
|
xor eax, eax
|
|
jmp @Exit1
|
|
|
|
@FindString:
|
|
sub esi, 2
|
|
mov [esp+12], esi
|
|
@FindString2:
|
|
cmp cl, [edx]
|
|
jz @Test0
|
|
@AfterTest0:
|
|
cmp cl, [edx+1]
|
|
jz @Test1
|
|
@AfterTest1:
|
|
add edx, 2
|
|
cmp edx, [esp+12]
|
|
jb @FindString4
|
|
cmp edx, [esp+8]
|
|
jb @FindString2
|
|
xor eax, eax
|
|
jmp @Exit1
|
|
|
|
@Test3:
|
|
add edx, 2
|
|
@Test1:
|
|
mov esi, [esp]
|
|
@Loop1:
|
|
movzx ebx, word ptr [esi+eax]
|
|
cmp bx, word ptr [esi+edx+1]
|
|
jnz @AfterTest1
|
|
add esi, 2
|
|
jl @Loop1
|
|
add edx, 2
|
|
xor eax, eax
|
|
cmp edx, [esp+8]
|
|
ja @Exit1
|
|
@RetCode1:
|
|
mov eax, edx
|
|
sub eax, [esp+4]
|
|
@Exit1:
|
|
add esp, 16
|
|
pop esi
|
|
pop ebx
|
|
ret
|
|
|
|
@Test2:
|
|
add edx,2
|
|
@Test0:
|
|
mov esi, [esp]
|
|
@Loop0:
|
|
movzx ebx, word ptr [esi+eax]
|
|
cmp bx, word ptr [esi+edx]
|
|
jnz @AfterTest0
|
|
add esi, 2
|
|
jl @Loop0
|
|
inc edx
|
|
@RetCode0:
|
|
mov eax, edx
|
|
sub eax, [esp+4]
|
|
add esp, 16
|
|
pop esi
|
|
pop ebx
|
|
end;
|
|
|
|
function Pos(const substr, str: WideString): Integer; overload;
|
|
asm
|
|
{ ->EAX Pointer to substr }
|
|
{ EDX Pointer to string }
|
|
{ <-EAX Position of substr in str or 0 }
|
|
|
|
TEST EAX,EAX
|
|
JE @@noWork
|
|
|
|
TEST EDX,EDX
|
|
JE @@stringEmpty
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX { Point ESI to substr }
|
|
MOV EDI,EDX { Point EDI to s }
|
|
|
|
MOV ECX,[EDI-4] { ECX = Length(s) }
|
|
SHR ECX,1
|
|
|
|
PUSH EDI { remember s position to calculate index }
|
|
|
|
MOV EDX,[ESI-4] { EDX = Length(substr) }
|
|
SHR EDX,1
|
|
|
|
DEC EDX { EDX = Length(substr) - 1 }
|
|
JS @@fail { < 0 ? return 0 }
|
|
MOV AX,[ESI] { AL = first char of substr }
|
|
ADD ESI,2 { Point ESI to 2'nd char of substr }
|
|
|
|
SUB ECX,EDX { #positions in s to look at }
|
|
{ = Length(s) - Length(substr) + 1 }
|
|
JLE @@fail
|
|
@@loop:
|
|
REPNE SCASW
|
|
JNE @@fail
|
|
MOV EBX,ECX { save outer loop counter }
|
|
PUSH ESI { save outer loop substr pointer }
|
|
PUSH EDI { save outer loop s pointer }
|
|
|
|
MOV ECX,EDX
|
|
REPE CMPSW
|
|
POP EDI { restore outer loop s pointer }
|
|
POP ESI { restore outer loop substr pointer }
|
|
JE @@found
|
|
MOV ECX,EBX { restore outer loop counter }
|
|
JMP @@loop
|
|
|
|
@@fail:
|
|
POP EDX { get rid of saved s pointer }
|
|
XOR EAX,EAX
|
|
JMP @@exit
|
|
|
|
@@stringEmpty:
|
|
XOR EAX,EAX
|
|
JMP @@noWork
|
|
|
|
@@found:
|
|
POP EDX { restore pointer to first char of s }
|
|
MOV EAX,EDI { EDI points of char after match }
|
|
SUB EAX,EDX { the difference is the correct index }
|
|
SHR EAX,1
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
@@noWork:
|
|
end;
|
|
|
|
// Don't use var param here - var ShortString is an open string param, which passes
|
|
// the ptr in EAX and the string's declared buffer length in EDX. Compiler codegen
|
|
// expects only two params for this call - ptr and newlength
|
|
|
|
procedure _SetLength(s: PShortString; newLength: Byte);
|
|
begin
|
|
Byte(s^[0]) := newLength; // should also fill new space
|
|
end;
|
|
|
|
procedure _SetString(s: PShortString; buffer: PChar; len: Byte);
|
|
begin
|
|
Byte(s^[0]) := len;
|
|
if buffer <> nil then
|
|
Move(buffer^, s^[1], len);
|
|
end;
|
|
|
|
procedure Randomize;
|
|
{$IFDEF LINUX}
|
|
begin
|
|
RandSeed := _time(nil);
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Counter: Int64;
|
|
begin
|
|
if QueryPerformanceCounter(Counter) then
|
|
RandSeed := Counter
|
|
else
|
|
RandSeed := GetTickCount;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function Random(const ARange: Integer): Integer;
|
|
{$IF DEFINED(CPU386)}
|
|
asm
|
|
{ ->EAX Range }
|
|
{ <-EAX Result }
|
|
PUSH EBX
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
CALL GetGOT
|
|
MOV EBX,EAX
|
|
POP EAX
|
|
MOV ECX,[EBX].OFFSET RandSeed
|
|
IMUL EDX,[ECX],08088405H
|
|
INC EDX
|
|
MOV [ECX],EDX
|
|
{$ELSE}
|
|
XOR EBX, EBX
|
|
IMUL EDX,[EBX].RandSeed,08088405H
|
|
INC EDX
|
|
MOV [EBX].RandSeed,EDX
|
|
{$ENDIF}
|
|
MUL EDX
|
|
MOV EAX,EDX
|
|
POP EBX
|
|
end;
|
|
{$ELSEIF DEFINED(CLR)}
|
|
begin
|
|
InitRandom;
|
|
Result := RandomEngine.Next(ARange);
|
|
end;
|
|
{$ELSE}
|
|
{$MESSAGE ERROR 'Random(Int):Int unimplemented'}
|
|
{$IFEND}
|
|
|
|
function Random: Extended;
|
|
{$IF DEFINED(CPU386)}
|
|
const two2neg32: double = ((1.0/$10000) / $10000); // 2^-32
|
|
asm
|
|
{ FUNCTION _RandExt: Extended; }
|
|
|
|
PUSH EBX
|
|
{$IFDEF PIC}
|
|
CALL GetGOT
|
|
MOV EBX,EAX
|
|
MOV ECX,[EBX].OFFSET RandSeed
|
|
IMUL EDX,[ECX],08088405H
|
|
INC EDX
|
|
MOV [ECX],EDX
|
|
{$ELSE}
|
|
XOR EBX, EBX
|
|
IMUL EDX,[EBX].RandSeed,08088405H
|
|
INC EDX
|
|
MOV [EBX].RandSeed,EDX
|
|
{$ENDIF}
|
|
|
|
FLD [EBX].two2neg32
|
|
PUSH 0
|
|
PUSH EDX
|
|
FILD qword ptr [ESP]
|
|
ADD ESP,8
|
|
FMULP ST(1), ST(0)
|
|
POP EBX
|
|
end;
|
|
{$ELSEIF DEFINED(CLR)}
|
|
begin
|
|
InitRandom;
|
|
Result := RandomEngine.NextDouble;
|
|
end;
|
|
{$ELSE}
|
|
{$MESSAGE ERROR 'Random:Extended unimplemented'}
|
|
{$IFEND}
|
|
|
|
procedure RmDir(const S: string);
|
|
begin
|
|
RmDir(PChar(s));
|
|
end;
|
|
|
|
procedure RmDir(P: PChar);
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
if not RemoveDirectory(P) then
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
if __rmdir(P) <> 0 then
|
|
{$ENDIF}
|
|
InOutError;
|
|
end;
|
|
|
|
function UpCase( ch : Char ) : Char;
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
Result := ch;
|
|
case Result of
|
|
'a'..'z': Dec(Result, Ord('a') - Ord('A'));
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> AL Character }
|
|
{ <- AL Result }
|
|
|
|
CMP AL,'a'
|
|
JB @@exit
|
|
CMP AL,'z'
|
|
JA @@exit
|
|
SUB AL,'a' - 'A'
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure Set8087CW(NewCW: Word);
|
|
begin
|
|
Default8087CW := NewCW;
|
|
asm
|
|
FNCLEX // don't raise pending exceptions enabled by the new flags
|
|
{$IFDEF PIC}
|
|
MOV EAX,[EBX].OFFSET Default8087CW
|
|
FLDCW [EAX]
|
|
{$ELSE}
|
|
FLDCW Default8087CW
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
function Get8087CW: Word;
|
|
asm
|
|
PUSH 0
|
|
FNSTCW [ESP].Word
|
|
POP EAX
|
|
end;
|
|
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ functions & procedures that need compiler magic }
|
|
{ ----------------------------------------------------- }
|
|
|
|
function Int(const X: Extended): Extended;
|
|
asm
|
|
FLD X
|
|
SUB ESP,4
|
|
FNSTCW [ESP].Word // save
|
|
FNSTCW [ESP+2].Word // scratch
|
|
FWAIT
|
|
OR [ESP+2].Word, $0F00 // trunc toward zero, full precision
|
|
FLDCW [ESP+2].Word
|
|
FRNDINT
|
|
FWAIT
|
|
FLDCW [ESP].Word
|
|
ADD ESP,4
|
|
end;
|
|
|
|
function Frac(const X: Extended): Extended;
|
|
asm
|
|
FLD X
|
|
FLD ST(0)
|
|
SUB ESP,4
|
|
FNSTCW [ESP].Word // save
|
|
FNSTCW [ESP+2].Word // scratch
|
|
FWAIT
|
|
OR [ESP+2].Word, $0F00 // trunc toward zero, full precision
|
|
FLDCW [ESP+2].Word
|
|
FRNDINT
|
|
FWAIT
|
|
FLDCW [ESP].Word
|
|
ADD ESP,4
|
|
FSUB
|
|
end;
|
|
|
|
function Exp(const X: Extended): Extended;
|
|
asm
|
|
{ e**x = 2**(x*log2(e)) }
|
|
FLD X
|
|
FLDL2E { y := x*log2e; }
|
|
FMUL
|
|
FLD ST(0) { i := round(y); }
|
|
FRNDINT
|
|
FSUB ST(1), ST { f := y - i; }
|
|
FXCH ST(1) { z := 2**f }
|
|
F2XM1
|
|
FLD1
|
|
FADD
|
|
FSCALE { result := z * 2**i }
|
|
FSTP ST(1)
|
|
end;
|
|
|
|
function Cos(const X: Extended): Extended;
|
|
asm
|
|
FLD X
|
|
FCOS
|
|
FWAIT
|
|
end;
|
|
|
|
function Sin(const X: Extended): Extended;
|
|
asm
|
|
FLD X
|
|
FSIN
|
|
FWAIT
|
|
end;
|
|
|
|
function Ln(const X: Extended): Extended;
|
|
asm
|
|
FLD X
|
|
FLDLN2
|
|
FXCH
|
|
FYL2X
|
|
FWAIT
|
|
end;
|
|
|
|
function ArcTan(const X: Extended): Extended;
|
|
asm
|
|
FLD X
|
|
FLD1
|
|
FPATAN
|
|
FWAIT
|
|
end;
|
|
|
|
function Sqrt(const X: Extended): Extended;
|
|
asm
|
|
FLD X
|
|
FSQRT
|
|
FWAIT
|
|
end;
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ functions & procedures that need compiler magic }
|
|
{ ----------------------------------------------------- }
|
|
|
|
procedure _ROUND;
|
|
asm
|
|
{ -> FST(0) Extended argument }
|
|
{ <- EDX:EAX Result }
|
|
|
|
SUB ESP,8
|
|
FISTP qword ptr [ESP]
|
|
FWAIT
|
|
POP EAX
|
|
POP EDX
|
|
end;
|
|
|
|
procedure _TRUNC;
|
|
asm
|
|
{ -> FST(0) Extended argument }
|
|
{ <- EDX:EAX Result }
|
|
|
|
SUB ESP,12
|
|
FNSTCW [ESP].Word // save
|
|
FNSTCW [ESP+2].Word // scratch
|
|
FWAIT
|
|
OR [ESP+2].Word, $0F00 // trunc toward zero, full precision
|
|
FLDCW [ESP+2].Word
|
|
FISTP qword ptr [ESP+4]
|
|
FWAIT
|
|
FLDCW [ESP].Word
|
|
POP ECX
|
|
POP EAX
|
|
POP EDX
|
|
end;
|
|
|
|
procedure _AbstractError;
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
asm
|
|
MOV EAX,210
|
|
JMP _RunError
|
|
end;
|
|
{$ELSE}
|
|
{$IFDEF PIC}
|
|
begin
|
|
if Assigned(AbstractErrorProc) then
|
|
AbstractErrorProc;
|
|
_RunError(210); // loses return address
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
CMP AbstractErrorProc, 0
|
|
JE @@NoAbstErrProc
|
|
CALL AbstractErrorProc
|
|
|
|
@@NoAbstErrProc:
|
|
MOV EAX,210
|
|
JMP _RunError
|
|
end;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
function TextOpen(var t: TTextRec): Integer; forward;
|
|
|
|
function OpenText(var t: TTextRec; Mode: Word): Integer;
|
|
begin
|
|
if (t.Mode < fmClosed) or (t.Mode > fmInOut) then
|
|
Result := 102
|
|
else
|
|
begin
|
|
if t.Mode <> fmClosed then _Close(t);
|
|
t.Mode := Mode;
|
|
if (t.Name[0] = #0) and (t.OpenFunc = nil) then // stdio
|
|
t.OpenFunc := @TextOpen;
|
|
Result := TTextIOFunc(t.OpenFunc)(t);
|
|
end;
|
|
if Result <> 0 then SetInOutRes(Result);
|
|
end;
|
|
|
|
function _ResetText(var t: TTextRec): Integer;
|
|
begin
|
|
Result := OpenText(t, fmInput);
|
|
end;
|
|
|
|
function _RewritText(var t: TTextRec): Integer;
|
|
begin
|
|
Result := OpenText(t, fmOutput);
|
|
end;
|
|
|
|
function _Append(var t: TTextRec): Integer;
|
|
begin
|
|
Result := OpenText(t, fmInOut);
|
|
end;
|
|
|
|
function TextIn(var t: TTextRec): Integer;
|
|
begin
|
|
t.BufEnd := 0;
|
|
t.BufPos := 0;
|
|
{$IFDEF LINUX}
|
|
t.BufEnd := __read(t.Handle, t.BufPtr, t.BufSize);
|
|
if Integer(t.BufEnd) = -1 then
|
|
begin
|
|
t.BufEnd := 0;
|
|
Result := GetLastError;
|
|
end
|
|
else
|
|
Result := 0;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
if ReadFile(t.Handle, t.BufPtr^, t.BufSize, t.BufEnd, nil) = 0 then
|
|
begin
|
|
Result := GetLastError;
|
|
if Result = 109 then
|
|
Result := 0; // NT quirk: got "broken pipe"? it's really eof
|
|
end
|
|
else
|
|
Result := 0;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function FileNOPProc(var t): Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
function TextOut(var t: TTextRec): Integer;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Dummy: Cardinal;
|
|
{$ENDIF}
|
|
begin
|
|
if t.BufPos = 0 then
|
|
Result := 0
|
|
else
|
|
begin
|
|
{$IFDEF LINUX}
|
|
if __write(t.Handle, t.BufPtr, t.BufPos) = Cardinal(-1) then
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
if WriteFile(t.Handle, t.BufPtr^, t.BufPos, Dummy, nil) = 0 then
|
|
{$ENDIF}
|
|
Result := GetLastError
|
|
else
|
|
Result := 0;
|
|
t.BufPos := 0;
|
|
end;
|
|
end;
|
|
|
|
function InternalClose(Handle: Integer): Boolean;
|
|
begin
|
|
{$IFDEF LINUX}
|
|
Result := __close(Handle) = 0;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
Result := CloseHandle(Handle) = 1;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TextClose(var t: TTextRec): Integer;
|
|
begin
|
|
t.Mode := fmClosed;
|
|
if not InternalClose(t.Handle) then
|
|
Result := GetLastError
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TextOpenCleanup(var t: TTextRec): Integer;
|
|
begin
|
|
InternalClose(t.Handle);
|
|
t.Mode := fmClosed;
|
|
Result := GetLastError;
|
|
end;
|
|
|
|
function TextOpen(var t: TTextRec): Integer;
|
|
{$IFDEF LINUX}
|
|
var
|
|
Flags: Integer;
|
|
Temp, I: Integer;
|
|
BytesRead: Integer;
|
|
begin
|
|
Result := 0;
|
|
t.BufPos := 0;
|
|
t.BufEnd := 0;
|
|
case t.Mode of
|
|
fmInput: // called by Reset
|
|
begin
|
|
Flags := O_RDONLY;
|
|
t.InOutFunc := @TextIn;
|
|
end;
|
|
fmOutput: // called by Rewrite
|
|
begin
|
|
Flags := O_CREAT or O_TRUNC or O_WRONLY;
|
|
t.InOutFunc := @TextOut;
|
|
end;
|
|
fmInOut: // called by Append
|
|
begin
|
|
Flags := O_APPEND or O_RDWR;
|
|
t.InOutFunc := @TextOut;
|
|
end;
|
|
else
|
|
Exit;
|
|
Flags := 0;
|
|
end;
|
|
|
|
t.FlushFunc := @FileNOPProc;
|
|
|
|
if t.Name[0] = #0 then // stdin or stdout
|
|
begin
|
|
t.BufPtr := @t.Buffer;
|
|
t.BufSize := sizeof(t.Buffer);
|
|
t.CloseFunc := @FileNOPProc;
|
|
if t.Mode = fmOutput then
|
|
begin
|
|
if @t = @ErrOutput then
|
|
t.Handle := STDERR_FILENO
|
|
else
|
|
t.Handle := STDOUT_FILENO;
|
|
t.FlushFunc := @TextOut;
|
|
end
|
|
else
|
|
t.Handle := STDIN_FILENO;
|
|
end
|
|
else
|
|
begin
|
|
t.CloseFunc := @TextClose;
|
|
|
|
Temp := __open(t.Name, Flags, FileAccessRights);
|
|
if Temp = -1 then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
|
|
t.Handle := Temp;
|
|
|
|
if t.Mode = fmInOut then // Append mode
|
|
begin
|
|
t.Mode := fmOutput;
|
|
|
|
if (t.flags and tfCRLF) <> 0 then // DOS mode, EOF significant
|
|
begin // scan for EOF char in last 128 byte sector.
|
|
Temp := _lseek(t.Handle, 0, SEEK_END);
|
|
if Temp = -1 then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
|
|
Dec(Temp, 128);
|
|
if Temp < 0 then Temp := 0;
|
|
|
|
if _lseek(t.Handle, Temp, SEEK_SET) = -1 then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
|
|
BytesRead := __read(t.Handle, t.BufPtr, 128);
|
|
if BytesRead = -1 then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
|
|
for I := 0 to BytesRead - 1 do
|
|
begin
|
|
if t.Buffer[I] = Char(cEOF) then
|
|
begin // truncate the file here
|
|
if _ftruncate(t.Handle, _lseek(t.Handle, I - BytesRead, SEEK_END)) = -1 then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
(*
|
|
var
|
|
OpenMode: Integer;
|
|
Flags, Std: ShortInt;
|
|
Temp: Integer;
|
|
I, BytesRead: Cardinal;
|
|
Mode: Byte;
|
|
begin
|
|
Result := 0;
|
|
if (t.Mode - fmInput) > (fmInOut - fmInput) then Exit;
|
|
Mode := t.Mode and 3;
|
|
t.BufPos := 0;
|
|
t.BufEnd := 0;
|
|
t.FlushFunc := @FileNOPProc;
|
|
|
|
if t.Name[0] = #0 then // stdin or stdout
|
|
begin
|
|
t.BufPtr := @t.Buffer;
|
|
t.BufSize := sizeof(t.Buffer);
|
|
t.CloseFunc := @FileNOPProc;
|
|
if Mode = (fmOutput and 3) then
|
|
begin
|
|
t.InOutFunc := @TextOut;
|
|
if @t = @ErrOutput then
|
|
Std := STD_ERROR_HANDLE
|
|
else
|
|
Std := STD_OUTPUT_HANDLE;
|
|
end
|
|
else
|
|
begin
|
|
t.InOutFunc := @TextIn;
|
|
Std := STD_INPUT_HANDLE;
|
|
end;
|
|
t.Handle := GetStdHandle(Std);
|
|
end
|
|
else
|
|
begin
|
|
t.CloseFunc := @TextClose;
|
|
|
|
Flags := OPEN_EXISTING;
|
|
if Mode = (fmInput and 3) then
|
|
begin // called by Reset
|
|
t.InOutFunc := @TextIn;
|
|
OpenMode := GENERIC_READ; // open for read
|
|
end
|
|
else
|
|
begin
|
|
t.InOutFunc := @TextOut;
|
|
if Mode = (fmInOut and 3) then // called by Append
|
|
OpenMode := GENERIC_READ OR GENERIC_WRITE // open for read/write
|
|
else
|
|
begin // called by Rewrite
|
|
OpenMode := GENERIC_WRITE; // open for write
|
|
Flags := CREATE_ALWAYS;
|
|
end;
|
|
end;
|
|
|
|
Temp := CreateFileA(t.Name, OpenMode, FILE_SHARE_READ, nil, Flags, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if Temp = -1 then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
|
|
t.Handle := Temp;
|
|
|
|
if Mode = (fmInOut and 3) then
|
|
begin
|
|
Dec(t.Mode); // fmInOut -> fmOutput
|
|
|
|
{; ??? we really have to look for the first eof byte in the
|
|
; ??? last record and truncate the file there.
|
|
; Not very nice and clean...
|
|
;
|
|
; lastRecPos = Max( GetFileSize(...) - 128, 0);
|
|
}
|
|
Temp := GetFileSize(t.Handle, 0);
|
|
if Temp = -1 then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
|
|
Dec(Temp, 128);
|
|
if Temp < 0 then Temp := 0;
|
|
|
|
if (SetFilePointer(t.Handle, Temp, nil, FILE_BEGIN) = -1) or
|
|
(ReadFile(t.Handle, t.Buffer, 128, BytesRead, nil) = 0) then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
|
|
for I := 0 to BytesRead do
|
|
begin
|
|
if t.Buffer[I] = Char(cEOF) then
|
|
begin // truncate the file here
|
|
if (SetFilePointer(t.Handle, I - BytesRead, nil, FILE_END) = -1) or
|
|
(not SetEndOfFile(t.Handle)) then
|
|
begin
|
|
Result := TextOpenCleanup(t);
|
|
Exit;
|
|
end;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
if Mode <> (fmInput and 3) then
|
|
begin
|
|
case GetFileType(t.Handle) of
|
|
0: begin // bad file type
|
|
TextOpenCleanup(t);
|
|
Result := 105;
|
|
Exit;
|
|
end;
|
|
2: t.FlushFunc := @TextOut;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
*)
|
|
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
|
|
PUSH ESI
|
|
|
|
MOV ESI,EAX
|
|
|
|
XOR EAX,EAX
|
|
MOV [ESI].TTextRec.BufPos,EAX
|
|
MOV [ESI].TTextRec.BufEnd,EAX
|
|
MOV AX,[ESI].TTextRec.Mode
|
|
|
|
SUB EAX,fmInput
|
|
JE @@calledByReset
|
|
|
|
DEC EAX
|
|
JE @@calledByRewrite
|
|
|
|
DEC EAX
|
|
JE @@calledByAppend
|
|
|
|
JMP @@exit
|
|
|
|
@@calledByReset:
|
|
|
|
MOV EAX,GENERIC_READ // open for read
|
|
MOV EDX,FILE_SHARE_READ
|
|
MOV ECX,OPEN_EXISTING
|
|
|
|
MOV [ESI].TTextRec.InOutFunc,offset TextIn
|
|
|
|
JMP @@common
|
|
|
|
@@calledByRewrite:
|
|
|
|
MOV EAX,GENERIC_WRITE // open for write
|
|
MOV EDX,FILE_SHARE_READ
|
|
MOV ECX,CREATE_ALWAYS
|
|
JMP @@commonOut
|
|
|
|
@@calledByAppend:
|
|
|
|
MOV EAX,GENERIC_READ OR GENERIC_WRITE // open for read/write
|
|
MOV EDX,FILE_SHARE_READ
|
|
MOV ECX,OPEN_EXISTING
|
|
|
|
@@commonOut:
|
|
|
|
MOV [ESI].TTextRec.InOutFunc,offset TextOut
|
|
|
|
@@common:
|
|
|
|
MOV [ESI].TTextRec.CloseFunc,offset TextClose
|
|
MOV [ESI].TTextRec.FlushFunc,offset FileNOPProc
|
|
CMP byte ptr [ESI].TTextRec.Name,0
|
|
JE @@isCon
|
|
|
|
// CreateFile(t.Name, EAX, EDX, Nil, ECX, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
PUSH 0
|
|
PUSH FILE_ATTRIBUTE_NORMAL
|
|
PUSH ECX
|
|
PUSH 0
|
|
PUSH EDX
|
|
PUSH EAX
|
|
LEA EAX,[ESI].TTextRec.Name
|
|
PUSH EAX
|
|
CALL CreateFileA
|
|
|
|
CMP EAX,-1
|
|
JZ @@error
|
|
|
|
MOV [ESI].TTextRec.Handle,EAX
|
|
CMP [ESI].TTextRec.Mode,fmInOut
|
|
JNE @@success
|
|
|
|
DEC [ESI].TTextRec.Mode // fmInOut -> fmOutput
|
|
|
|
{; ??? we really have to look for the first eof byte in the
|
|
; ??? last record and truncate the file there.
|
|
; Not very nice and clean...
|
|
;
|
|
; lastRecPos = Max( GetFileSize(...) - 128, 0);
|
|
}
|
|
PUSH 0
|
|
PUSH [ESI].TTextRec.Handle
|
|
CALL GetFileSize
|
|
|
|
INC EAX
|
|
JZ @@error
|
|
SUB EAX,129
|
|
JNC @@3
|
|
XOR EAX,EAX
|
|
@@3:
|
|
// lseek(f.Handle, SEEK_SET, lastRecPos);
|
|
|
|
PUSH FILE_BEGIN
|
|
PUSH 0
|
|
PUSH EAX
|
|
PUSH [ESI].TTextRec.Handle
|
|
CALL SetFilePointer
|
|
|
|
INC EAX
|
|
JE @@error
|
|
|
|
// bytesRead = read(f.Handle, f.Buffer, 128);
|
|
|
|
PUSH 0
|
|
MOV EDX,ESP
|
|
PUSH 0
|
|
PUSH EDX
|
|
PUSH 128
|
|
LEA EDX,[ESI].TTextRec.Buffer
|
|
PUSH EDX
|
|
PUSH [ESI].TTextRec.Handle
|
|
CALL ReadFile
|
|
POP EDX
|
|
DEC EAX
|
|
JNZ @@error
|
|
|
|
// for (i = 0; i < bytesRead; i++)
|
|
|
|
XOR EAX,EAX
|
|
@@loop:
|
|
CMP EAX,EDX
|
|
JAE @@success
|
|
|
|
// if (f.Buffer[i] == eof)
|
|
|
|
CMP byte ptr [ESI].TTextRec.Buffer[EAX],eof
|
|
JE @@truncate
|
|
INC EAX
|
|
JMP @@loop
|
|
|
|
@@truncate:
|
|
|
|
// lseek( f.Handle, SEEK_END, i - bytesRead );
|
|
|
|
PUSH FILE_END
|
|
PUSH 0
|
|
SUB EAX,EDX
|
|
PUSH EAX
|
|
PUSH [ESI].TTextRec.Handle
|
|
CALL SetFilePointer
|
|
INC EAX
|
|
JE @@error
|
|
|
|
// SetEndOfFile( f.Handle );
|
|
|
|
PUSH [ESI].TTextRec.Handle
|
|
CALL SetEndOfFile
|
|
DEC EAX
|
|
JNE @@error
|
|
|
|
JMP @@success
|
|
|
|
@@isCon:
|
|
LEA EAX,[ESI].TTextRec.Buffer
|
|
MOV [ESI].TTextRec.BufSize, TYPE TTextRec.Buffer
|
|
MOV [ESI].TTextRec.CloseFunc,offset FileNOPProc
|
|
MOV [ESI].TTextRec.BufPtr,EAX
|
|
CMP [ESI].TTextRec.Mode,fmOutput
|
|
JE @@output
|
|
PUSH STD_INPUT_HANDLE
|
|
JMP @@1
|
|
@@output:
|
|
CMP ESI,offset ErrOutput
|
|
JNE @@stdout
|
|
PUSH STD_ERROR_HANDLE
|
|
JMP @@1
|
|
@@stdout:
|
|
PUSH STD_OUTPUT_HANDLE
|
|
@@1:
|
|
CALL GetStdHandle
|
|
CMP EAX,-1
|
|
JE @@error
|
|
MOV [ESI].TTextRec.Handle,EAX
|
|
|
|
@@success:
|
|
CMP [ESI].TTextRec.Mode,fmInput
|
|
JE @@2
|
|
PUSH [ESI].TTextRec.Handle
|
|
CALL GetFileType
|
|
TEST EAX,EAX
|
|
JE @@badFileType
|
|
CMP EAX,2
|
|
JNE @@2
|
|
MOV [ESI].TTextRec.FlushFunc,offset TextOut
|
|
@@2:
|
|
XOR EAX,EAX
|
|
@@exit:
|
|
POP ESI
|
|
RET
|
|
|
|
@@badFileType:
|
|
PUSH [ESI].TTextRec.Handle
|
|
CALL CloseHandle
|
|
MOV [ESI].TTextRec.Mode,fmClosed
|
|
MOV EAX,105
|
|
JMP @@exit
|
|
|
|
@@error:
|
|
MOV [ESI].TTextRec.Mode,fmClosed
|
|
CALL GetLastError
|
|
JMP @@exit
|
|
end;
|
|
{$ENDIF}
|
|
|
|
const
|
|
fNameLen = 260;
|
|
|
|
function _Assign(var t: TTextRec; const s: String): Integer;
|
|
begin
|
|
FillChar(t, sizeof(TFileRec), 0);
|
|
t.BufPtr := @t.Buffer;
|
|
t.Mode := fmClosed;
|
|
t.Flags := tfCRLF * Byte(DefaultTextLineBreakStyle);
|
|
t.BufSize := sizeof(t.Buffer);
|
|
t.OpenFunc := @TextOpen;
|
|
Move(S[1], t.Name, Length(s));
|
|
t.Name[Length(s)] := #0;
|
|
Result := 0;
|
|
end;
|
|
|
|
function InternalFlush(var t: TTextRec; Func: TTextIOFunc): Integer;
|
|
begin
|
|
case t.Mode of
|
|
fmOutput,
|
|
fmInOut : Result := Func(t);
|
|
fmInput : Result := 0;
|
|
else
|
|
if (@t = @Output) or (@t = @ErrOutput) then
|
|
Result := 0
|
|
else
|
|
Result := 103;
|
|
end;
|
|
if Result <> 0 then SetInOutRes(Result);
|
|
end;
|
|
|
|
function Flush(var t: Text): Integer;
|
|
begin
|
|
Result := InternalFlush(TTextRec(t), TTextRec(t).InOutFunc);
|
|
end;
|
|
|
|
function _Flush(var t: TTextRec): Integer;
|
|
begin
|
|
Result := InternalFlush(t, t.FlushFunc);
|
|
end;
|
|
|
|
type
|
|
{$IFDEF MSWINDOWS}
|
|
TIOProc = function (hFile: Integer; Buffer: Pointer; nNumberOfBytesToWrite: Cardinal;
|
|
var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Integer; stdcall;
|
|
|
|
function ReadFileX(hFile: Integer; Buffer: Pointer; nNumberOfBytesToRead: Cardinal;
|
|
var lpNumberOfBytesRead: Cardinal; lpOverlapped: Pointer): Integer; stdcall;
|
|
external kernel name 'ReadFile';
|
|
function WriteFileX(hFile: Integer; Buffer: Pointer; nNumberOfBytesToWrite: Cardinal;
|
|
var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Integer; stdcall;
|
|
external kernel name 'WriteFile';
|
|
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
TIOProc = function (Handle: Integer; Buffer: Pointer; Count: Cardinal): Cardinal; cdecl;
|
|
{$ENDIF}
|
|
|
|
function BlockIO(var f: TFileRec; buffer: Pointer; recCnt: Cardinal; var recsDone: Longint;
|
|
ModeMask: Integer; IOProc: TIOProc; ErrorNo: Integer): Cardinal;
|
|
// Note: RecsDone ptr can be nil!
|
|
begin
|
|
if (f.Mode and ModeMask) = ModeMask then // fmOutput or fmInOut / fmInput or fmInOut
|
|
begin
|
|
{$IFDEF LINUX}
|
|
Result := IOProc(f.Handle, buffer, recCnt * f.RecSize);
|
|
if Integer(Result) = -1 then
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
if IOProc(f.Handle, buffer, recCnt * f.RecSize, Result, nil) = 0 then
|
|
{$ENDIF}
|
|
begin
|
|
SetInOutRes(GetLastError);
|
|
Result := 0;
|
|
end
|
|
else
|
|
begin
|
|
Result := Result div f.RecSize;
|
|
if @RecsDone <> nil then
|
|
RecsDone := Result
|
|
else if Result <> recCnt then
|
|
begin
|
|
SetInOutRes(ErrorNo);
|
|
Result := 0;
|
|
end
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
SetInOutRes(103); // file not open
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
|
|
function _BlockRead(var f: TFileRec; buffer: Pointer; recCnt: Longint; var recsRead: Longint): Longint;
|
|
begin
|
|
Result := BlockIO(f, buffer, recCnt, recsRead, fmInput,
|
|
{$IFDEF MSWINDOWS} ReadFileX, {$ENDIF}
|
|
{$IFDEF LINUX} __read, {$ENDIF}
|
|
100);
|
|
end;
|
|
|
|
function _BlockWrite(var f: TFileRec; buffer: Pointer; recCnt: Longint; var recsWritten: Longint): Longint;
|
|
begin
|
|
Result := BlockIO(f, buffer, recCnt, recsWritten, fmOutput,
|
|
{$IFDEF MSWINDOWS} WriteFileX, {$ENDIF}
|
|
{$IFDEF LINUX} __write, {$ENDIF}
|
|
101);
|
|
end;
|
|
|
|
function _Close(var t: TTextRec): Integer;
|
|
begin
|
|
Result := 0;
|
|
if (t.Mode >= fmInput) and (t.Mode <= fmInOut) then
|
|
begin
|
|
if (t.Mode and fmOutput) = fmOutput then // fmOutput or fmInOut
|
|
Result := TTextIOFunc(t.InOutFunc)(t);
|
|
if Result = 0 then
|
|
Result := TTextIOFunc(t.CloseFunc)(t);
|
|
if Result <> 0 then
|
|
SetInOutRes(Result);
|
|
end
|
|
else
|
|
if @t <> @Input then
|
|
SetInOutRes(103);
|
|
end;
|
|
|
|
procedure _PStrCat;
|
|
asm
|
|
{ ->EAX = Pointer to destination string }
|
|
{ EDX = Pointer to source string }
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
{ load dest len into EAX }
|
|
|
|
MOV EDI,EAX
|
|
XOR EAX,EAX
|
|
MOV AL,[EDI]
|
|
|
|
{ load source address in ESI, source len in ECX }
|
|
|
|
MOV ESI,EDX
|
|
XOR ECX,ECX
|
|
MOV CL,[ESI]
|
|
INC ESI
|
|
|
|
{ calculate final length in DL and store it in the destination }
|
|
|
|
MOV DL,AL
|
|
ADD DL,CL
|
|
JC @@trunc
|
|
|
|
@@cont:
|
|
MOV [EDI],DL
|
|
|
|
{ calculate final dest address }
|
|
|
|
INC EDI
|
|
ADD EDI,EAX
|
|
|
|
{ do the copy }
|
|
|
|
REP MOVSB
|
|
|
|
{ done }
|
|
|
|
POP EDI
|
|
POP ESI
|
|
RET
|
|
|
|
@@trunc:
|
|
INC DL { DL = #chars to truncate }
|
|
SUB CL,DL { CL = source len - #chars to truncate }
|
|
MOV DL,255 { DL = maximum length }
|
|
JMP @@cont
|
|
end;
|
|
|
|
procedure _PStrNCat;
|
|
asm
|
|
{ ->EAX = Pointer to destination string }
|
|
{ EDX = Pointer to source string }
|
|
{ CL = max length of result (allocated size of dest - 1) }
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
{ load dest len into EAX }
|
|
|
|
MOV EDI,EAX
|
|
XOR EAX,EAX
|
|
MOV AL,[EDI]
|
|
|
|
{ load source address in ESI, source len in EDX }
|
|
|
|
MOV ESI,EDX
|
|
XOR EDX,EDX
|
|
MOV DL,[ESI]
|
|
INC ESI
|
|
|
|
{ calculate final length in AL and store it in the destination }
|
|
|
|
ADD AL,DL
|
|
JC @@trunc
|
|
CMP AL,CL
|
|
JA @@trunc
|
|
|
|
@@cont:
|
|
MOV ECX,EDX
|
|
MOV DL,[EDI]
|
|
MOV [EDI],AL
|
|
|
|
{ calculate final dest address }
|
|
|
|
INC EDI
|
|
ADD EDI,EDX
|
|
|
|
{ do the copy }
|
|
|
|
REP MOVSB
|
|
|
|
@@done:
|
|
POP EDI
|
|
POP ESI
|
|
RET
|
|
|
|
@@trunc:
|
|
{ CL = maxlen }
|
|
|
|
MOV AL,CL { AL = final length = maxlen }
|
|
SUB CL,[EDI] { CL = length to copy = maxlen - destlen }
|
|
JBE @@done
|
|
MOV DL,CL
|
|
JMP @@cont
|
|
end;
|
|
|
|
procedure _PStrCpy(Dest: PShortString; Source: PShortString);
|
|
begin
|
|
Move(Source^, Dest^, Byte(Source^[0])+1);
|
|
end;
|
|
|
|
procedure _PStrNCpy(Dest: PShortString; Source: PShortString; MaxLen: Byte);
|
|
begin
|
|
if MaxLen > Byte(Source^[0]) then
|
|
MaxLen := Byte(Source^[0]);
|
|
Byte(Dest^[0]) := MaxLen;
|
|
Move(Source^[1], Dest^[1], MaxLen);
|
|
end;
|
|
|
|
procedure _PStrCmp;
|
|
asm
|
|
{ ->EAX = Pointer to left string }
|
|
{ EDX = Pointer to right string }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
|
|
XOR EAX,EAX
|
|
XOR EDX,EDX
|
|
MOV AL,[ESI]
|
|
MOV DL,[EDI]
|
|
INC ESI
|
|
INC EDI
|
|
|
|
SUB EAX,EDX { eax = len1 - len2 }
|
|
JA @@skip1
|
|
ADD EDX,EAX { edx = len2 + (len1 - len2) = len1 }
|
|
|
|
@@skip1:
|
|
PUSH EDX
|
|
SHR EDX,2
|
|
JE @@cmpRest
|
|
@@longLoop:
|
|
MOV ECX,[ESI]
|
|
MOV EBX,[EDI]
|
|
CMP ECX,EBX
|
|
JNE @@misMatch
|
|
DEC EDX
|
|
JE @@cmpRestP4
|
|
MOV ECX,[ESI+4]
|
|
MOV EBX,[EDI+4]
|
|
CMP ECX,EBX
|
|
JNE @@misMatch
|
|
ADD ESI,8
|
|
ADD EDI,8
|
|
DEC EDX
|
|
JNE @@longLoop
|
|
JMP @@cmpRest
|
|
@@cmpRestP4:
|
|
ADD ESI,4
|
|
ADD EDI,4
|
|
@@cmpRest:
|
|
POP EDX
|
|
AND EDX,3
|
|
JE @@equal
|
|
|
|
MOV CL,[ESI]
|
|
CMP CL,[EDI]
|
|
JNE @@exit
|
|
DEC EDX
|
|
JE @@equal
|
|
MOV CL,[ESI+1]
|
|
CMP CL,[EDI+1]
|
|
JNE @@exit
|
|
DEC EDX
|
|
JE @@equal
|
|
MOV CL,[ESI+2]
|
|
CMP CL,[EDI+2]
|
|
JNE @@exit
|
|
|
|
@@equal:
|
|
ADD EAX,EAX
|
|
JMP @@exit
|
|
|
|
@@misMatch:
|
|
POP EDX
|
|
CMP CL,BL
|
|
JNE @@exit
|
|
CMP CH,BH
|
|
JNE @@exit
|
|
SHR ECX,16
|
|
SHR EBX,16
|
|
CMP CL,BL
|
|
JNE @@exit
|
|
CMP CH,BH
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _AStrCmp;
|
|
asm
|
|
{ ->EAX = Pointer to left string }
|
|
{ EDX = Pointer to right string }
|
|
{ ECX = Number of chars to compare}
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH ECX
|
|
MOV ESI,ECX
|
|
SHR ESI,2
|
|
JE @@cmpRest
|
|
|
|
@@longLoop:
|
|
MOV ECX,[EAX]
|
|
MOV EBX,[EDX]
|
|
CMP ECX,EBX
|
|
JNE @@misMatch
|
|
DEC ESI
|
|
JE @@cmpRestP4
|
|
MOV ECX,[EAX+4]
|
|
MOV EBX,[EDX+4]
|
|
CMP ECX,EBX
|
|
JNE @@misMatch
|
|
ADD EAX,8
|
|
ADD EDX,8
|
|
DEC ESI
|
|
JNE @@longLoop
|
|
JMP @@cmpRest
|
|
@@cmpRestp4:
|
|
ADD EAX,4
|
|
ADD EDX,4
|
|
@@cmpRest:
|
|
POP ESI
|
|
AND ESI,3
|
|
JE @@exit
|
|
|
|
MOV CL,[EAX]
|
|
CMP CL,[EDX]
|
|
JNE @@exit
|
|
DEC ESI
|
|
JE @@equal
|
|
MOV CL,[EAX+1]
|
|
CMP CL,[EDX+1]
|
|
JNE @@exit
|
|
DEC ESI
|
|
JE @@equal
|
|
MOV CL,[EAX+2]
|
|
CMP CL,[EDX+2]
|
|
JNE @@exit
|
|
|
|
@@equal:
|
|
XOR EAX,EAX
|
|
JMP @@exit
|
|
|
|
@@misMatch:
|
|
POP ESI
|
|
CMP CL,BL
|
|
JNE @@exit
|
|
CMP CH,BH
|
|
JNE @@exit
|
|
SHR ECX,16
|
|
SHR EBX,16
|
|
CMP CL,BL
|
|
JNE @@exit
|
|
CMP CH,BH
|
|
|
|
@@exit:
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
function _EofFile(var f: TFileRec): Boolean;
|
|
begin
|
|
Result := _FilePos(f) >= _FileSize(f);
|
|
end;
|
|
|
|
function _EofText(var t: TTextRec): Boolean;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// <- AL Boolean result
|
|
CMP [EAX].TTextRec.Mode,fmInput
|
|
JNE @@readChar
|
|
MOV EDX,[EAX].TTextRec.BufPos
|
|
CMP EDX,[EAX].TTextRec.BufEnd
|
|
JAE @@readChar
|
|
ADD EDX,[EAX].TTextRec.BufPtr
|
|
TEST [EAX].TTextRec.Flags,tfCRLF
|
|
JZ @@FalseExit
|
|
MOV CL,[EDX]
|
|
CMP CL,cEof
|
|
JNZ @@FalseExit
|
|
|
|
@@eof:
|
|
MOV AL,1
|
|
JMP @@exit
|
|
|
|
@@readChar:
|
|
PUSH EAX
|
|
CALL _ReadChar
|
|
POP EDX
|
|
CMP AH,cEof
|
|
JE @@eof
|
|
DEC [EDX].TTextRec.BufPos
|
|
@@FalseExit:
|
|
XOR EAX,EAX
|
|
@@exit:
|
|
end;
|
|
|
|
function _Eoln(var t: TTextRec): Boolean;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// <- AL Boolean result
|
|
|
|
CMP [EAX].TTextRec.Mode,fmInput
|
|
JNE @@readChar
|
|
MOV EDX,[EAX].TTextRec.BufPos
|
|
CMP EDX,[EAX].TTextRec.BufEnd
|
|
JAE @@readChar
|
|
ADD EDX,[EAX].TTextRec.BufPtr
|
|
TEST [EAX].TTextRec.Flags,tfCRLF
|
|
MOV AL,0
|
|
MOV CL,[EDX]
|
|
JZ @@testLF
|
|
CMP CL,cCR
|
|
JE @@eol
|
|
CMP CL,cEOF
|
|
JE @@eof
|
|
JMP @@exit
|
|
|
|
@@testLF:
|
|
CMP CL,cLF
|
|
JE @@eol
|
|
CMP CL,cEOF
|
|
JE @@eof
|
|
JMP @@exit
|
|
|
|
@@readChar:
|
|
PUSH EAX
|
|
CALL _ReadChar
|
|
POP EDX
|
|
CMP AH,cEOF
|
|
JE @@eof
|
|
DEC [EDX].TTextRec.BufPos
|
|
XOR ECX,ECX
|
|
XCHG ECX,EAX
|
|
TEST [EDX].TTextRec.Mode,tfCRLF
|
|
JNE @@testLF
|
|
CMP CL,cCR
|
|
JE @@eol
|
|
JMP @@exit
|
|
|
|
@@eol:
|
|
@@eof:
|
|
MOV AL,1
|
|
@@exit:
|
|
end;
|
|
|
|
procedure _Erase(var f: TFileRec);
|
|
begin
|
|
if (f.Mode < fmClosed) or (f.Mode > fmInOut) then
|
|
SetInOutRes(102) // file not assigned
|
|
else
|
|
{$IFDEF LINUX}
|
|
if __remove(f.Name) < 0 then
|
|
SetInOutRes(GetLastError);
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
if not DeleteFileA(f.Name) then
|
|
SetInOutRes(GetLastError);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
// Floating-point divide reverse routine
|
|
// ST(1) = ST(0) / ST(1), pop ST
|
|
|
|
procedure _FSafeDivideR;
|
|
asm
|
|
FXCH
|
|
JMP _FSafeDivide
|
|
end;
|
|
|
|
// Floating-point divide routine
|
|
// ST(1) = ST(1) / ST(0), pop ST
|
|
|
|
procedure _FSafeDivide;
|
|
type
|
|
Z = packed record // helper type to make parameter references more readable
|
|
Dividend: Extended; // (TBYTE PTR [ESP])
|
|
Pad: Word;
|
|
Divisor: Extended; // (TBYTE PTR [ESP+12])
|
|
end;
|
|
asm
|
|
CMP TestFDIV,0 //Check FDIV indicator
|
|
JLE @@FDivideChecked //Jump if flawed or don't know
|
|
FDIV //Known to be ok, so just do FDIV
|
|
RET
|
|
|
|
// FDIV constants
|
|
@@FDIVRiscTable: DB 0,1,0,0,4,0,0,7,0,0,10,0,0,13,0,0;
|
|
|
|
@@FDIVScale1: DD $3F700000 // 0.9375
|
|
@@FDIVScale2: DD $3F880000 // 1.0625
|
|
@@FDIV1SHL63: DD $5F000000 // 1 SHL 63
|
|
|
|
@@TestDividend: DD $C0000000,$4150017E // 4195835.0
|
|
@@TestDivisor: DD $80000000,$4147FFFF // 3145727.0
|
|
@@TestOne: DD $00000000,$3FF00000 // 1.0
|
|
|
|
// Flawed FDIV detection
|
|
@@FDivideDetect:
|
|
MOV TestFDIV,1 //Indicate correct FDIV
|
|
PUSH EAX
|
|
SUB ESP,12
|
|
FSTP TBYTE PTR [ESP] //Save off ST
|
|
FLD QWORD PTR @@TestDividend //Ok if x - (x / y) * y < 1.0
|
|
FDIV QWORD PTR @@TestDivisor
|
|
FMUL QWORD PTR @@TestDivisor
|
|
FSUBR QWORD PTR @@TestDividend
|
|
FCOMP QWORD PTR @@TestOne
|
|
FSTSW AX
|
|
SHR EAX,7
|
|
AND EAX,002H //Zero if FDIV is flawed
|
|
DEC EAX
|
|
MOV TestFDIV,AL //1 means Ok, -1 means flawed
|
|
FLD TBYTE PTR [ESP] //Restore ST
|
|
ADD ESP,12
|
|
POP EAX
|
|
JMP _FSafeDivide
|
|
|
|
@@FDivideChecked:
|
|
JE @@FDivideDetect //Do detection if TestFDIV = 0
|
|
|
|
@@1: PUSH EAX
|
|
SUB ESP,24
|
|
FSTP [ESP].Z.Divisor //Store Divisor and Dividend
|
|
FSTP [ESP].Z.Dividend
|
|
FLD [ESP].Z.Dividend
|
|
FLD [ESP].Z.Divisor
|
|
@@2: MOV EAX,DWORD PTR [ESP+4].Z.Divisor //Is Divisor a denormal?
|
|
ADD EAX,EAX
|
|
JNC @@20 //Yes, @@20
|
|
XOR EAX,0E000000H //If these three bits are not all
|
|
TEST EAX,0E000000H //ones, FDIV will work
|
|
JZ @@10 //Jump if all ones
|
|
@@3: FDIV //Do FDIV and exit
|
|
ADD ESP,24
|
|
POP EAX
|
|
RET
|
|
|
|
@@10: SHR EAX,28 //If the four bits following the MSB
|
|
//of the mantissa have a decimal
|
|
//of 1, 4, 7, 10, or 13, FDIV may
|
|
CMP byte ptr @@FDIVRiscTable[EAX],0 //not work correctly
|
|
JZ @@3 //Do FDIV if not 1, 4, 7, 10, or 13
|
|
MOV EAX,DWORD PTR [ESP+8].Z.Divisor //Get Divisor exponent
|
|
AND EAX,7FFFH
|
|
JZ @@3 //Ok to FDIV if denormal
|
|
CMP EAX,7FFFH
|
|
JE @@3 //Ok to FDIV if NAN or INF
|
|
MOV EAX,DWORD PTR [ESP+8].Z.Dividend //Get Dividend exponent
|
|
AND EAX,7FFFH
|
|
CMP EAX,1 //Small number?
|
|
JE @@11 //Yes, @@11
|
|
FMUL DWORD PTR @@FDIVScale1 //Scale by 15/16
|
|
FXCH
|
|
FMUL DWORD PTR @@FDIVScale1
|
|
FXCH
|
|
JMP @@3 //FDIV is now safe
|
|
|
|
@@11: FMUL DWORD PTR @@FDIVScale2 //Scale by 17/16
|
|
FXCH
|
|
FMUL DWORD PTR @@FDIVScale2
|
|
FXCH
|
|
JMP @@3 //FDIV is now safe
|
|
|
|
@@20: MOV EAX,DWORD PTR [ESP].Z.Divisor //Is entire Divisor zero?
|
|
OR EAX,DWORD PTR [ESP+4].Z.Divisor
|
|
JZ @@3 //Yes, ok to FDIV
|
|
MOV EAX,DWORD PTR [ESP+8].Z.Divisor //Get Divisor exponent
|
|
AND EAX,7FFFH //Non-zero exponent is invalid
|
|
JNZ @@3 //Ok to FDIV if invalid
|
|
MOV EAX,DWORD PTR [ESP+8].Z.Dividend //Get Dividend exponent
|
|
AND EAX,7FFFH //Denormal?
|
|
JZ @@21 //Yes, @@21
|
|
CMP EAX,7FFFH //NAN or INF?
|
|
JE @@3 //Yes, ok to FDIV
|
|
MOV EAX,DWORD PTR [ESP+4].Z.Dividend //If MSB of mantissa is zero,
|
|
ADD EAX,EAX //the number is invalid
|
|
JNC @@3 //Ok to FDIV if invalid
|
|
JMP @@22
|
|
@@21: MOV EAX,DWORD PTR [ESP+4].Z.Dividend //If MSB of mantissa is zero,
|
|
ADD EAX,EAX //the number is invalid
|
|
JC @@3 //Ok to FDIV if invalid
|
|
@@22: FXCH //Scale stored Divisor image by
|
|
FSTP ST(0) //1 SHL 63 and restart
|
|
FLD ST(0)
|
|
FMUL DWORD PTR @@FDIV1SHL63
|
|
FSTP [ESP].Z.Divisor
|
|
FLD [ESP].Z.Dividend
|
|
FXCH
|
|
JMP @@2
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _FilePos(var f: TFileRec): Longint;
|
|
begin
|
|
if (f.Mode > fmClosed) and (f.Mode <= fmInOut) then
|
|
begin
|
|
{$IFDEF LINUX}
|
|
Result := _lseek(f.Handle, 0, SEEK_CUR);
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
Result := SetFilePointer(f.Handle, 0, nil, FILE_CURRENT);
|
|
{$ENDIF}
|
|
if Result = -1 then
|
|
InOutError
|
|
else
|
|
Result := Cardinal(Result) div f.RecSize;
|
|
end
|
|
else
|
|
begin
|
|
SetInOutRes(103);
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
function _FileSize(var f: TFileRec): Longint;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result := -1;
|
|
if (f.Mode > fmClosed) and (f.Mode <= fmInOut) then
|
|
begin
|
|
Result := GetFileSize(f.Handle, 0);
|
|
if Result = -1 then
|
|
InOutError
|
|
else
|
|
Result := Cardinal(Result) div f.RecSize;
|
|
end
|
|
else
|
|
SetInOutRes(103);
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
var
|
|
stat: TStatStruct;
|
|
begin
|
|
Result := -1;
|
|
if (f.Mode > fmClosed) and (f.Mode <= fmInOut) then
|
|
begin
|
|
if _fxstat(STAT_VER_LINUX, f.Handle, stat) <> 0 then
|
|
InOutError
|
|
else
|
|
Result := stat.st_size div f.RecSize;
|
|
end
|
|
else
|
|
SetInOutRes(103);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _FillChar(var Dest; count: Integer; Value: Char);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
I: Integer;
|
|
P: PChar;
|
|
begin
|
|
P := PChar(@Dest);
|
|
for I := count-1 downto 0 do
|
|
P[I] := Value;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ ->EAX Pointer to destination }
|
|
{ EDX count }
|
|
{ CL value }
|
|
|
|
PUSH EDI
|
|
|
|
MOV EDI,EAX { Point EDI to destination }
|
|
|
|
MOV CH,CL { Fill EAX with value repeated 4 times }
|
|
MOV EAX,ECX
|
|
SHL EAX,16
|
|
MOV AX,CX
|
|
|
|
MOV ECX,EDX
|
|
SAR ECX,2
|
|
JS @@exit
|
|
|
|
REP STOSD { Fill count DIV 4 dwords }
|
|
|
|
MOV ECX,EDX
|
|
AND ECX,3
|
|
REP STOSB { Fill count MOD 4 bytes }
|
|
|
|
@@exit:
|
|
POP EDI
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure Mark;
|
|
begin
|
|
Error(reInvalidPtr);
|
|
end;
|
|
|
|
procedure _RandInt;
|
|
asm
|
|
{ ->EAX Range }
|
|
{ <-EAX Result }
|
|
PUSH EBX
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
CALL GetGOT
|
|
MOV EBX,EAX
|
|
POP EAX
|
|
MOV ECX,[EBX].OFFSET RandSeed
|
|
IMUL EDX,[ECX],08088405H
|
|
INC EDX
|
|
MOV [ECX],EDX
|
|
{$ELSE}
|
|
XOR EBX, EBX
|
|
IMUL EDX,[EBX].RandSeed,08088405H
|
|
INC EDX
|
|
MOV [EBX].RandSeed,EDX
|
|
{$ENDIF}
|
|
MUL EDX
|
|
MOV EAX,EDX
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _RandExt;
|
|
const two2neg32: double = ((1.0/$10000) / $10000); // 2^-32
|
|
asm
|
|
{ FUNCTION _RandExt: Extended; }
|
|
|
|
PUSH EBX
|
|
{$IFDEF PIC}
|
|
CALL GetGOT
|
|
MOV EBX,EAX
|
|
MOV ECX,[EBX].OFFSET RandSeed
|
|
IMUL EDX,[ECX],08088405H
|
|
INC EDX
|
|
MOV [ECX],EDX
|
|
{$ELSE}
|
|
XOR EBX, EBX
|
|
IMUL EDX,[EBX].RandSeed,08088405H
|
|
INC EDX
|
|
MOV [EBX].RandSeed,EDX
|
|
{$ENDIF}
|
|
|
|
FLD [EBX].two2neg32
|
|
PUSH 0
|
|
PUSH EDX
|
|
FILD qword ptr [ESP]
|
|
ADD ESP,8
|
|
FMULP ST(1), ST(0)
|
|
POP EBX
|
|
end;
|
|
|
|
function _ReadRec(var f: TFileRec; Buffer: Pointer): Integer;
|
|
{$IFDEF LINUX}
|
|
begin
|
|
if (f.Mode and fmInput) = fmInput then // fmInput or fmInOut
|
|
begin
|
|
Result := __read(f.Handle, Buffer, f.RecSize);
|
|
if Result = -1 then
|
|
InOutError
|
|
else if Cardinal(Result) <> f.RecSize then
|
|
SetInOutRes(100);
|
|
end
|
|
else
|
|
begin
|
|
SetInOutRes(103); // file not open for input
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
// -> EAX Pointer to file variable
|
|
// EDX Pointer to buffer
|
|
|
|
PUSH EBX
|
|
XOR ECX,ECX
|
|
MOV EBX,EAX
|
|
MOV CX,[EAX].TFileRec.Mode // File must be open
|
|
SUB ECX,fmInput
|
|
JE @@skip
|
|
SUB ECX,fmInOut-fmInput
|
|
JNE @@fileNotOpen
|
|
@@skip:
|
|
|
|
// ReadFile(f.Handle, buffer, f.RecSize, @result, Nil);
|
|
|
|
PUSH 0 // space for OS result
|
|
MOV EAX,ESP
|
|
|
|
PUSH 0 // pass lpOverlapped
|
|
PUSH EAX // pass @result
|
|
|
|
PUSH [EBX].TFileRec.RecSize // pass nNumberOfBytesToRead
|
|
|
|
PUSH EDX // pass lpBuffer
|
|
PUSH [EBX].TFileRec.Handle // pass hFile
|
|
CALL ReadFile
|
|
POP EDX // pop result
|
|
DEC EAX // check EAX = TRUE
|
|
JNZ @@error
|
|
|
|
CMP EDX,[EBX].TFileRec.RecSize // result = f.RecSize ?
|
|
JE @@exit
|
|
|
|
@@readError:
|
|
MOV EAX,100
|
|
JMP @@errExit
|
|
|
|
@@fileNotOpen:
|
|
MOV EAX,103
|
|
JMP @@errExit
|
|
|
|
@@error:
|
|
CALL GetLastError
|
|
@@errExit:
|
|
CALL SetInOutRes
|
|
@@exit:
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
// If the file is Input std variable, try to open it
|
|
// Otherwise, runtime error.
|
|
function TryOpenForInput(var t: TTextRec): Boolean;
|
|
begin
|
|
if @t = @Input then
|
|
begin
|
|
t.Flags := tfCRLF * Byte(DefaultTextLineBreakStyle);
|
|
_ResetText(t);
|
|
end;
|
|
|
|
Result := t.Mode = fmInput;
|
|
if not Result then
|
|
SetInOutRes(104);
|
|
end;
|
|
|
|
function _ReadChar(var t: TTextRec): Char;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// <- AL Character read. (may be a pseudo cEOF in DOS mode)
|
|
// <- AH cEOF = End of File, else 0
|
|
// For eof, #$1A is returned in AL and in AH.
|
|
// For errors, InOutRes is set and #$1A is returned.
|
|
|
|
CMP [EAX].TTextRec.Mode, fmInput
|
|
JE @@checkBuf
|
|
PUSH EAX
|
|
CALL TryOpenForInput
|
|
TEST AL,AL
|
|
POP EAX
|
|
JZ @@eofexit
|
|
|
|
@@checkBuf:
|
|
MOV EDX,[EAX].TTextRec.BufPos
|
|
CMP EDX,[EAX].TTextRec.BufEnd
|
|
JAE @@fillBuf
|
|
@@cont:
|
|
TEST [EAX].TTextRec.Flags,tfCRLF
|
|
MOV ECX,[EAX].TTextRec.BufPtr
|
|
MOV CL,[ECX+EDX]
|
|
JZ @@cont2
|
|
CMP CL,cEof // Check for EOF char in DOS mode
|
|
JE @@eofexit
|
|
@@cont2:
|
|
INC EDX
|
|
MOV [EAX].TTextRec.BufPos,EDX
|
|
XOR EAX,EAX
|
|
JMP @@exit
|
|
|
|
@@fillBuf:
|
|
PUSH EAX
|
|
CALL [EAX].TTextRec.InOutFunc
|
|
TEST EAX,EAX
|
|
JNE @@error
|
|
POP EAX
|
|
MOV EDX,[EAX].TTextRec.BufPos
|
|
CMP EDX,[EAX].TTextRec.BufEnd
|
|
JB @@cont
|
|
|
|
// We didn't get characters. Must be eof then.
|
|
|
|
@@eof:
|
|
TEST [EAX].TTextRec.Flags,tfCRLF
|
|
JZ @@eofexit
|
|
// In DOS CRLF compatibility mode, synthesize an EOF char
|
|
// Store one eof in the buffer and increment BufEnd
|
|
MOV ECX,[EAX].TTextRec.BufPtr
|
|
MOV byte ptr [ECX+EDX],cEof
|
|
INC [EAX].TTextRec.BufEnd
|
|
JMP @@eofexit
|
|
|
|
@@error:
|
|
CALL SetInOutRes
|
|
POP EAX
|
|
@@eofexit:
|
|
MOV CL,cEof
|
|
MOV AH,CL
|
|
@@exit:
|
|
MOV AL,CL
|
|
end;
|
|
|
|
function _ReadLong(var t: TTextRec): Longint;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// <- EAX Result
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
SUB ESP,36 // var numbuf: String[32];
|
|
|
|
MOV ESI,EAX
|
|
CALL _SeekEof
|
|
DEC AL
|
|
JZ @@eof
|
|
|
|
MOV EDI,ESP // EDI -> numBuf[0]
|
|
MOV BL,32
|
|
@@loop:
|
|
MOV EAX,ESI
|
|
CALL _ReadChar
|
|
CMP AL,' '
|
|
JBE @@endNum
|
|
STOSB
|
|
DEC BL
|
|
JNZ @@loop
|
|
@@convert:
|
|
MOV byte ptr [EDI],0
|
|
MOV EAX,ESP // EAX -> numBuf
|
|
PUSH EDX // allocate code result
|
|
MOV EDX,ESP // pass pointer to code
|
|
CALL _ValLong // convert
|
|
POP EDX // pop code result into EDX
|
|
TEST EDX,EDX
|
|
JZ @@exit
|
|
MOV EAX,106
|
|
CALL SetInOutRes
|
|
JMP @@exit
|
|
|
|
@@endNum:
|
|
CMP AH,cEof
|
|
JE @@convert
|
|
DEC [ESI].TTextRec.BufPos
|
|
JMP @@convert
|
|
|
|
@@eof:
|
|
XOR EAX,EAX
|
|
@@exit:
|
|
ADD ESP,36
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
function ReadLine(var t: TTextRec; buf: Pointer; maxLen: Longint): Pointer;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// EDX Pointer to buffer
|
|
// ECX Maximum count of chars to read
|
|
// <- ECX Actual count of chars in buffer
|
|
// <- EAX Pointer to text record
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH ECX
|
|
MOV ESI,ECX
|
|
MOV EDI,EDX
|
|
|
|
CMP [EAX].TTextRec.Mode,fmInput
|
|
JE @@start
|
|
PUSH EAX
|
|
CALL TryOpenForInput
|
|
TEST AL,AL
|
|
POP EAX
|
|
JZ @@exit
|
|
|
|
@@start:
|
|
MOV EBX,EAX
|
|
|
|
TEST ESI,ESI
|
|
JLE @@exit
|
|
|
|
MOV EDX,[EBX].TTextRec.BufPos
|
|
MOV ECX,[EBX].TTextRec.BufEnd
|
|
SUB ECX,EDX
|
|
ADD EDX,[EBX].TTextRec.BufPtr
|
|
|
|
@@loop:
|
|
DEC ECX
|
|
JL @@readChar
|
|
MOV AL,[EDX]
|
|
INC EDX
|
|
@@cont:
|
|
CMP AL,cLF
|
|
JE @@lf
|
|
|
|
CMP AL,cCR
|
|
JE @@cr
|
|
|
|
STOSB
|
|
DEC ESI
|
|
JG @@loop
|
|
JMP @@finish
|
|
|
|
@@cr:
|
|
MOV AL,[EDX]
|
|
CMP AL,cLF
|
|
JNE @@loop
|
|
@@lf:
|
|
DEC EDX
|
|
@@finish:
|
|
SUB EDX,[EBX].TTextRec.BufPtr
|
|
MOV [EBX].TTextRec.BufPos,EDX
|
|
JMP @@exit
|
|
|
|
@@readChar:
|
|
MOV [EBX].TTextRec.BufPos,EDX
|
|
MOV EAX,EBX
|
|
CALL _ReadChar
|
|
MOV EDX,[EBX].TTextRec.BufPos
|
|
MOV ECX,[EBX].TTextRec.BufEnd
|
|
SUB ECX,EDX
|
|
ADD EDX,[EBX].TTextRec.BufPtr
|
|
TEST AH,AH //eof
|
|
JZ @@cont
|
|
|
|
@@exit:
|
|
MOV EAX,EBX
|
|
POP ECX
|
|
SUB ECX,ESI
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _ReadString(var t: TTextRec; s: PShortString; maxLen: Longint);
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// EDX Pointer to string
|
|
// ECX Maximum length of string
|
|
|
|
PUSH EDX
|
|
INC EDX
|
|
CALL ReadLine
|
|
POP EDX
|
|
MOV [EDX],CL
|
|
end;
|
|
|
|
procedure _ReadCString(var t: TTextRec; s: PChar; maxLen: Longint);
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// EDX Pointer to string
|
|
// ECX Maximum length of string
|
|
|
|
PUSH EDX
|
|
CALL ReadLine
|
|
POP EDX
|
|
MOV byte ptr [EDX+ECX],0
|
|
end;
|
|
|
|
procedure _ReadLString(var t: TTextRec; var s: AnsiString);
|
|
asm
|
|
{ -> EAX pointer to Text }
|
|
{ EDX pointer to AnsiString }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
|
|
MOV EAX,EDX
|
|
CALL _LStrClr
|
|
|
|
SUB ESP,256
|
|
|
|
MOV EAX,EBX
|
|
MOV EDX,ESP
|
|
MOV ECX,255
|
|
CALL _ReadString
|
|
|
|
MOV EAX,ESI
|
|
MOV EDX,ESP
|
|
CALL _LStrFromString
|
|
|
|
CMP byte ptr [ESP],255
|
|
JNE @@exit
|
|
@@loop:
|
|
|
|
MOV EAX,EBX
|
|
MOV EDX,ESP
|
|
MOV ECX,255
|
|
CALL _ReadString
|
|
|
|
MOV EDX,ESP
|
|
PUSH 0
|
|
MOV EAX,ESP
|
|
CALL _LStrFromString
|
|
|
|
MOV EAX,ESI
|
|
MOV EDX,[ESP]
|
|
CALL _LStrCat
|
|
|
|
MOV EAX,ESP
|
|
CALL _LStrClr
|
|
POP EAX
|
|
|
|
CMP byte ptr [ESP],255
|
|
JE @@loop
|
|
|
|
@@exit:
|
|
ADD ESP,256
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
function IsValidMultibyteChar(const Src: PChar; SrcBytes: Integer): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
const
|
|
ERROR_NO_UNICODE_TRANSLATION = 1113; // Win32 GetLastError when result = 0
|
|
MB_ERR_INVALID_CHARS = 8;
|
|
var
|
|
Dest: WideChar;
|
|
begin
|
|
Result := MultiByteToWideChar(0, MB_ERR_INVALID_CHARS, Src, SrcBytes, @Dest, 1) <> 0;
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
begin
|
|
Result := mblen(Src, SrcBytes) <> -1;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
function _ReadWChar(var t: TTextRec): WideChar;
|
|
var
|
|
scratch: array [0..7] of AnsiChar;
|
|
wc: WideChar;
|
|
i: Integer;
|
|
begin
|
|
i := 0;
|
|
while i < High(scratch) do
|
|
begin
|
|
scratch[i] := _ReadChar(t);
|
|
Inc(i);
|
|
scratch[i] := #0;
|
|
if IsValidMultibyteChar(scratch, i) then
|
|
begin
|
|
WCharFromChar(@wc, 1, scratch, i);
|
|
Result := wc;
|
|
Exit;
|
|
end;
|
|
end;
|
|
SetInOutRes(106); // Invalid Input
|
|
Result := #0;
|
|
end;
|
|
|
|
|
|
procedure _ReadWCString(var t: TTextRec; s: PWideChar; maxBytes: Longint);
|
|
var
|
|
i, maxLen: Integer;
|
|
wc: WideChar;
|
|
begin
|
|
if s = nil then Exit;
|
|
i := 0;
|
|
maxLen := maxBytes div sizeof(WideChar);
|
|
while i < maxLen do
|
|
begin
|
|
wc := _ReadWChar(t);
|
|
case Integer(wc) of
|
|
cEOF: if _EOFText(t) then Break;
|
|
cLF : begin
|
|
Dec(t.BufPos);
|
|
Break;
|
|
end;
|
|
cCR : if Byte(t.BufPtr[t.BufPos]) = cLF then
|
|
begin
|
|
Dec(t.BufPos);
|
|
Break;
|
|
end;
|
|
end;
|
|
s[i] := wc;
|
|
Inc(i);
|
|
end;
|
|
s[i] := #0;
|
|
end;
|
|
|
|
procedure _ReadWString(var t: TTextRec; var s: WideString);
|
|
var
|
|
Temp: AnsiString;
|
|
begin
|
|
_ReadLString(t, Temp);
|
|
s := Temp;
|
|
end;
|
|
|
|
function _ReadExt(var t: TTextRec): Extended;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// <- FST(0) Result
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
SUB ESP,68 // var numbuf: array[0..64] of char;
|
|
|
|
MOV ESI,EAX
|
|
CALL _SeekEof
|
|
DEC AL
|
|
JZ @@eof
|
|
|
|
MOV EDI,ESP // EDI -> numBuf[0]
|
|
MOV BL,64
|
|
@@loop:
|
|
MOV EAX,ESI
|
|
CALL _ReadChar
|
|
CMP AL,' '
|
|
JBE @@endNum
|
|
STOSB
|
|
DEC BL
|
|
JNZ @@loop
|
|
@@convert:
|
|
MOV byte ptr [EDI],0
|
|
MOV EAX,ESP // EAX -> numBuf
|
|
PUSH EDX // allocate code result
|
|
MOV EDX,ESP // pass pointer to code
|
|
CALL _ValExt // convert
|
|
POP EDX // pop code result into EDX
|
|
TEST EDX,EDX
|
|
JZ @@exit
|
|
MOV EAX,106
|
|
CALL SetInOutRes
|
|
JMP @@exit
|
|
|
|
@@endNum:
|
|
CMP AH,cEOF
|
|
JE @@convert
|
|
DEC [ESI].TTextRec.BufPos
|
|
JMP @@convert
|
|
|
|
@@eof:
|
|
FLDZ
|
|
@@exit:
|
|
ADD ESP,68
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _ReadLn(var t: TTextRec);
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
|
|
PUSH EBX
|
|
MOV EBX,EAX
|
|
@@loop:
|
|
MOV EAX,EBX
|
|
CALL _ReadChar
|
|
|
|
CMP AL,cLF // accept LF as end of line
|
|
JE @@exit
|
|
CMP AH,cEOF
|
|
JE @@eof
|
|
CMP AL,cCR
|
|
JNE @@loop
|
|
|
|
MOV EAX,EBX
|
|
CALL _ReadChar
|
|
|
|
CMP AL,cLF // accept CR+LF as end of line
|
|
JE @@exit
|
|
CMP AH,cEOF // accept CR+EOF as end of line
|
|
JE @@eof
|
|
DEC [EBX].TTextRec.BufPos
|
|
JMP @@loop // else CR+ anything else is not a line break.
|
|
|
|
@@exit:
|
|
@@eof:
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _Rename(var f: TFileRec; newName: PChar);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if f.Mode = fmClosed then
|
|
begin
|
|
if newName = nil then newName := '';
|
|
{$IFDEF LINUX}
|
|
if __rename(f.Name, newName) = 0 then
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
if MoveFileA(f.Name, newName) then
|
|
{$ENDIF}
|
|
begin
|
|
I := 0;
|
|
while (newName[I] <> #0) and (I < High(f.Name)) do
|
|
begin
|
|
f.Name[I] := newName[I];
|
|
Inc(I);
|
|
end
|
|
end
|
|
else
|
|
SetInOutRes(GetLastError);
|
|
end
|
|
else
|
|
SetInOutRes(102);
|
|
end;
|
|
|
|
procedure Release;
|
|
begin
|
|
Error(reInvalidPtr);
|
|
end;
|
|
|
|
function _CloseFile(var f: TFileRec): Integer;
|
|
begin
|
|
f.Mode := fmClosed;
|
|
Result := 0;
|
|
if not InternalClose(f.Handle) then
|
|
begin
|
|
InOutError;
|
|
Result := 1;
|
|
end;
|
|
end;
|
|
|
|
function OpenFile(var f: TFileRec; recSiz: Longint; mode: Longint): Integer;
|
|
{$IFDEF LINUX}
|
|
var
|
|
Flags: Integer;
|
|
begin
|
|
Result := 0;
|
|
if (f.Mode >= fmClosed) and (f.Mode <= fmInOut) then
|
|
begin
|
|
if f.Mode <> fmClosed then // not yet closed: close it
|
|
begin
|
|
Result := TFileIOFunc(f.CloseFunc)(f);
|
|
if Result <> 0 then
|
|
SetInOutRes(Result);
|
|
end;
|
|
|
|
if recSiz <= 0 then
|
|
SetInOutRes(106);
|
|
|
|
f.RecSize := recSiz;
|
|
f.InOutFunc := @FileNopProc;
|
|
|
|
if f.Name[0] <> #0 then
|
|
begin
|
|
f.CloseFunc := @_CloseFile;
|
|
case mode of
|
|
1: begin
|
|
Flags := O_APPEND or O_WRONLY;
|
|
f.Mode := fmOutput;
|
|
end;
|
|
2: begin
|
|
Flags := O_RDWR;
|
|
f.Mode := fmInOut;
|
|
end;
|
|
3: begin
|
|
Flags := O_CREAT or O_TRUNC or O_RDWR;
|
|
f.Mode := fmInOut;
|
|
end;
|
|
else
|
|
Flags := O_RDONLY;
|
|
f.Mode := fmInput;
|
|
end;
|
|
|
|
f.Handle := __open(f.Name, Flags, FileAccessRights);
|
|
end
|
|
else // stdin or stdout
|
|
begin
|
|
f.CloseFunc := @FileNopProc;
|
|
if mode = 3 then
|
|
f.Handle := STDOUT_FILENO
|
|
else
|
|
f.Handle := STDIN_FILENO;
|
|
end;
|
|
|
|
if f.Handle = -1 then
|
|
begin
|
|
f.Mode := fmClosed;
|
|
InOutError;
|
|
end;
|
|
end
|
|
else
|
|
SetInOutRes(102);
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
const
|
|
ShareTab: array [0..7] of Integer =
|
|
(FILE_SHARE_READ OR FILE_SHARE_WRITE, // OF_SHARE_COMPAT 0x00000000
|
|
0, // OF_SHARE_EXCLUSIVE 0x00000010
|
|
FILE_SHARE_READ, // OF_SHARE_DENY_WRITE 0x00000020
|
|
FILE_SHARE_WRITE, // OF_SHARE_DENY_READ 0x00000030
|
|
FILE_SHARE_READ OR FILE_SHARE_WRITE, // OF_SHARE_DENY_NONE 0x00000040
|
|
0,0,0);
|
|
asm
|
|
//-> EAX Pointer to file record
|
|
// EDX Record size
|
|
// ECX File mode
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
XOR EDX,EDX
|
|
MOV EBX,EAX
|
|
|
|
MOV DX,[EAX].TFileRec.Mode
|
|
SUB EDX,fmClosed
|
|
JE @@alreadyClosed
|
|
CMP EDX,fmInOut-fmClosed
|
|
JA @@notAssignedError
|
|
|
|
// not yet closed: close it. File parameter is still in EAX
|
|
|
|
CALL [EBX].TFileRec.CloseFunc
|
|
TEST EAX,EAX
|
|
JE @@alreadyClosed
|
|
CALL SetInOutRes
|
|
|
|
@@alreadyClosed:
|
|
|
|
MOV [EBX].TFileRec.Mode,fmInOut
|
|
MOV [EBX].TFileRec.RecSize,ESI
|
|
MOV [EBX].TFileRec.CloseFunc,offset _CloseFile
|
|
MOV [EBX].TFileRec.InOutFunc,offset FileNopProc
|
|
|
|
CMP byte ptr [EBX].TFileRec.Name,0
|
|
JE @@isCon
|
|
|
|
MOV EAX,GENERIC_READ OR GENERIC_WRITE
|
|
MOV DL,FileMode
|
|
AND EDX,070H
|
|
SHR EDX,4-2
|
|
MOV EDX,dword ptr [shareTab+EDX]
|
|
MOV ECX,CREATE_ALWAYS
|
|
|
|
SUB EDI,3
|
|
JE @@calledByRewrite
|
|
|
|
MOV ECX,OPEN_EXISTING
|
|
INC EDI
|
|
JE @@skip
|
|
|
|
MOV EAX,GENERIC_WRITE
|
|
INC EDI
|
|
MOV [EBX].TFileRec.Mode,fmOutput
|
|
JE @@skip
|
|
|
|
MOV EAX,GENERIC_READ
|
|
MOV [EBX].TFileRec.Mode,fmInput
|
|
|
|
@@skip:
|
|
@@calledByRewrite:
|
|
|
|
// CreateFile(t.FileName, EAX, EDX, Nil, ECX, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
PUSH 0
|
|
PUSH FILE_ATTRIBUTE_NORMAL
|
|
PUSH ECX
|
|
PUSH 0
|
|
PUSH EDX
|
|
PUSH EAX
|
|
LEA EAX,[EBX].TFileRec.Name
|
|
PUSH EAX
|
|
CALL CreateFileA
|
|
@@checkHandle:
|
|
CMP EAX,-1
|
|
JZ @@error
|
|
|
|
MOV [EBX].TFileRec.Handle,EAX
|
|
JMP @@exit
|
|
|
|
@@isCon:
|
|
MOV [EBX].TFileRec.CloseFunc,offset FileNopProc
|
|
CMP EDI,3
|
|
JE @@output
|
|
PUSH STD_INPUT_HANDLE
|
|
JMP @@1
|
|
@@output:
|
|
PUSH STD_OUTPUT_HANDLE
|
|
@@1:
|
|
CALL GetStdHandle
|
|
JMP @@checkHandle
|
|
|
|
@@notAssignedError:
|
|
MOV EAX,102
|
|
JMP @@errExit
|
|
|
|
@@error:
|
|
MOV [EBX].TFileRec.Mode,fmClosed
|
|
CALL GetLastError
|
|
@@errExit:
|
|
CALL SetInOutRes
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _ResetFile(var f: TFileRec; recSize: Longint): Integer;
|
|
var
|
|
m: Byte;
|
|
begin
|
|
m := FileMode and 3;
|
|
if m > 2 then m := 2;
|
|
Result := OpenFile(f, recSize, m);
|
|
end;
|
|
|
|
function _RewritFile(var f: TFileRec; recSize: Longint): Integer;
|
|
begin
|
|
Result := OpenFile(f, recSize, 3);
|
|
end;
|
|
|
|
procedure _Seek(var f: TFileRec; recNum: Cardinal);
|
|
{$IFDEF LINUX}
|
|
begin
|
|
if (f.Mode >= fmInput) and (f.Mode <= fmInOut) then
|
|
begin
|
|
if _lseek(f.Handle, f.RecSize * recNum, SEEK_SET) = -1 then
|
|
InOutError;
|
|
end
|
|
else
|
|
SetInOutRes(103);
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
// -> EAX Pointer to file variable
|
|
// EDX Record number
|
|
|
|
MOV ECX,EAX
|
|
MOVZX EAX,[EAX].TFileRec.Mode // check whether file is open
|
|
SUB EAX,fmInput
|
|
CMP EAX,fmInOut-fmInput
|
|
JA @@fileNotOpen
|
|
|
|
// SetFilePointer(f.Handle, recNum*f.RecSize, FILE_BEGIN
|
|
PUSH FILE_BEGIN // pass dwMoveMethod
|
|
MOV EAX,[ECX].TFileRec.RecSize
|
|
MUL EDX
|
|
PUSH 0 // pass lpDistanceToMoveHigh
|
|
PUSH EAX // pass lDistanceToMove
|
|
PUSH [ECX].TFileRec.Handle // pass hFile
|
|
CALL SetFilePointer // get current position
|
|
INC EAX
|
|
JZ InOutError
|
|
JMP @@exit
|
|
|
|
@@fileNotOpen:
|
|
MOV EAX,103
|
|
JMP SetInOutRes
|
|
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _SeekEof(var t: TTextRec): Boolean;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// <- AL Boolean result
|
|
|
|
PUSH EBX
|
|
MOV EBX,EAX
|
|
@@loop:
|
|
MOV EAX,EBX
|
|
CALL _ReadChar
|
|
CMP AL,' '
|
|
JA @@endloop
|
|
CMP AH,cEOF
|
|
JE @@eof
|
|
JMP @@loop
|
|
@@eof:
|
|
MOV AL,1
|
|
JMP @@exit
|
|
|
|
@@endloop:
|
|
DEC [EBX].TTextRec.BufPos
|
|
XOR AL,AL
|
|
@@exit:
|
|
POP EBX
|
|
end;
|
|
|
|
function _SeekEoln(var t: TTextRec): Boolean;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// <- AL Boolean result
|
|
|
|
PUSH EBX
|
|
MOV EBX,EAX
|
|
@@loop:
|
|
MOV EAX,EBX
|
|
CALL _ReadChar
|
|
CMP AL,' '
|
|
JA @@falseExit
|
|
CMP AH,cEOF
|
|
JE @@eof
|
|
CMP AL,cLF
|
|
JE @@trueExit
|
|
CMP AL,cCR
|
|
JNE @@loop
|
|
|
|
@@trueExit:
|
|
MOV AL,1
|
|
JMP @@exitloop
|
|
|
|
@@falseExit:
|
|
XOR AL,AL
|
|
@@exitloop:
|
|
DEC [EBX].TTextRec.BufPos
|
|
JMP @@exit
|
|
|
|
@@eof:
|
|
MOV AL,1
|
|
@@exit:
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _SetTextBuf(var t: TTextRec; p: Pointer; size: Longint);
|
|
begin
|
|
t.BufPtr := P;
|
|
t.BufSize := size;
|
|
t.BufPos := 0;
|
|
t.BufEnd := 0;
|
|
end;
|
|
|
|
procedure _StrLong(val, width: Longint; s: PShortString);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
I: Integer;
|
|
sign: Longint;
|
|
a: array [0..19] of char;
|
|
P: PChar;
|
|
begin
|
|
sign := val;
|
|
val := Abs(val);
|
|
I := 0;
|
|
repeat
|
|
a[I] := Chr((val mod 10) + Ord('0'));
|
|
Inc(I);
|
|
val := val div 10;
|
|
until val = 0;
|
|
|
|
if sign < 0 then
|
|
begin
|
|
a[I] := '-';
|
|
Inc(I);
|
|
end;
|
|
|
|
if width < I then
|
|
width := I;
|
|
if width > 255 then
|
|
width := 255;
|
|
s^[0] := Chr(width);
|
|
P := @S^[1];
|
|
while width > I do
|
|
begin
|
|
P^ := ' ';
|
|
Inc(P);
|
|
Dec(width);
|
|
end;
|
|
repeat
|
|
Dec(I);
|
|
P^ := a[I];
|
|
Inc(P);
|
|
until I <= 0;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ PROCEDURE _StrLong( val: Longint; width: Longint; VAR s: ShortString );
|
|
->EAX Value
|
|
EDX Width
|
|
ECX Pointer to string }
|
|
|
|
PUSH EBX { VAR i: Longint; }
|
|
PUSH ESI { VAR sign : Longint; }
|
|
PUSH EDI
|
|
PUSH EDX { store width on the stack }
|
|
SUB ESP,20 { VAR a: array [0..19] of Char; }
|
|
|
|
MOV EDI,ECX
|
|
|
|
MOV ESI,EAX { sign := val }
|
|
|
|
CDQ { val := Abs(val); canned sequence }
|
|
XOR EAX,EDX
|
|
SUB EAX,EDX
|
|
|
|
MOV ECX,10
|
|
XOR EBX,EBX { i := 0; }
|
|
|
|
@@repeat1: { repeat }
|
|
XOR EDX,EDX { a[i] := Chr( val MOD 10 + Ord('0') );}
|
|
|
|
DIV ECX { val := val DIV 10; }
|
|
|
|
ADD EDX,'0'
|
|
MOV [ESP+EBX],DL
|
|
INC EBX { i := i + 1; }
|
|
TEST EAX,EAX { until val = 0; }
|
|
JNZ @@repeat1
|
|
|
|
TEST ESI,ESI
|
|
JGE @@2
|
|
MOV byte ptr [ESP+EBX],'-'
|
|
INC EBX
|
|
@@2:
|
|
MOV [EDI],BL { s^++ := Chr(i); }
|
|
INC EDI
|
|
|
|
MOV ECX,[ESP+20] { spaceCnt := width - i; }
|
|
CMP ECX,255
|
|
JLE @@3
|
|
MOV ECX,255
|
|
@@3:
|
|
SUB ECX,EBX
|
|
JLE @@repeat2 { for k := 1 to spaceCnt do s^++ := ' '; }
|
|
ADD [EDI-1],CL
|
|
MOV AL,' '
|
|
REP STOSB
|
|
|
|
@@repeat2: { repeat }
|
|
MOV AL,[ESP+EBX-1] { s^ := a[i-1]; }
|
|
MOV [EDI],AL
|
|
INC EDI { s := s + 1 }
|
|
DEC EBX { i := i - 1; }
|
|
JNZ @@repeat2 { until i = 0; }
|
|
|
|
ADD ESP,20+4
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _Str0Long(val: Longint; s: PShortString);
|
|
begin
|
|
_StrLong(val, 0, s);
|
|
end;
|
|
|
|
procedure _Truncate(var f: TFileRec);
|
|
{$IFDEF LINUX}
|
|
begin
|
|
if (f.Mode and fmOutput) = fmOutput then // fmOutput or fmInOut
|
|
begin
|
|
if _ftruncate(f.Handle, _lseek(f.Handle, 0, SEEK_CUR)) = -1 then
|
|
InOutError;
|
|
end
|
|
else
|
|
SetInOutRes(103);
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
// -> EAX Pointer to text or file variable
|
|
|
|
MOVZX EDX,[EAX].TFileRec.Mode // check whether file is open
|
|
SUB EDX,fmInput
|
|
CMP EDX,fmInOut-fmInput
|
|
JA @@fileNotOpen
|
|
|
|
PUSH [EAX].TFileRec.Handle
|
|
CALL SetEndOfFile
|
|
DEC EAX
|
|
JZ @@exit
|
|
JMP InOutError
|
|
|
|
@@fileNotOpen:
|
|
MOV EAX,103
|
|
JMP SetInOutRes
|
|
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _ValLong(const s: String; var code: Integer): Longint;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
I: Integer;
|
|
Negative, Hex: Boolean;
|
|
begin
|
|
I := 1;
|
|
code := -1;
|
|
Result := 0;
|
|
Negative := False;
|
|
Hex := False;
|
|
while (I <= Length(s)) and (s[I] = ' ') do Inc(I);
|
|
if I > Length(s) then Exit;
|
|
case s[I] of
|
|
'$',
|
|
'x',
|
|
'X': begin
|
|
Hex := True;
|
|
Inc(I);
|
|
end;
|
|
'0': begin
|
|
Hex := (Length(s) > I) and (UpCase(s[I+1]) = 'X');
|
|
if Hex then Inc(I,2);
|
|
end;
|
|
'-': begin
|
|
Negative := True;
|
|
Inc(I);
|
|
end;
|
|
'+': Inc(I);
|
|
end;
|
|
if Hex then
|
|
while I <= Length(s) do
|
|
begin
|
|
if Result > (High(Result) div 16) then
|
|
begin
|
|
code := I;
|
|
Exit;
|
|
end;
|
|
case s[I] of
|
|
'0'..'9': Result := Result * 16 + Ord(s[I]) - Ord('0');
|
|
'a'..'f': Result := Result * 16 + Ord(s[I]) - Ord('a') + 10;
|
|
'A'..'F': Result := Result * 16 + Ord(s[I]) - Ord('A') + 10;
|
|
else
|
|
code := I;
|
|
Exit;
|
|
end;
|
|
end
|
|
else
|
|
while I <= Length(s) do
|
|
begin
|
|
if Result > (High(Result) div 10) then
|
|
begin
|
|
code := I;
|
|
Exit;
|
|
end;
|
|
Result := Result * 10 + Ord(s[I]) - Ord('0');
|
|
Inc(I);
|
|
end;
|
|
if Negative then
|
|
Result := -Result;
|
|
code := 0;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ FUNCTION _ValLong( s: AnsiString; VAR code: Integer ) : Longint; }
|
|
{ ->EAX Pointer to string }
|
|
{ EDX Pointer to code result }
|
|
{ <-EAX Result }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
PUSH EAX { save for the error case }
|
|
|
|
TEST EAX,EAX
|
|
JE @@empty
|
|
|
|
XOR EAX,EAX
|
|
XOR EBX,EBX
|
|
MOV EDI,07FFFFFFFH / 10 { limit }
|
|
|
|
@@blankLoop:
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
CMP BL,' '
|
|
JE @@blankLoop
|
|
|
|
@@endBlanks:
|
|
MOV CH,0
|
|
CMP BL,'-'
|
|
JE @@minus
|
|
CMP BL,'+'
|
|
JE @@plus
|
|
CMP BL,'$'
|
|
JE @@dollar
|
|
|
|
CMP BL, 'x'
|
|
JE @@dollar
|
|
CMP BL, 'X'
|
|
JE @@dollar
|
|
CMP BL, '0'
|
|
JNE @@firstDigit
|
|
MOV BL, [ESI]
|
|
INC ESI
|
|
CMP BL, 'x'
|
|
JE @@dollar
|
|
CMP BL, 'X'
|
|
JE @@dollar
|
|
TEST BL, BL
|
|
JE @@endDigits
|
|
JMP @@digLoop
|
|
|
|
@@firstDigit:
|
|
TEST BL,BL
|
|
JE @@error
|
|
|
|
@@digLoop:
|
|
SUB BL,'0'
|
|
CMP BL,9
|
|
JA @@error
|
|
CMP EAX,EDI { value > limit ? }
|
|
JA @@overFlow
|
|
LEA EAX,[EAX+EAX*4]
|
|
ADD EAX,EAX
|
|
ADD EAX,EBX { fortunately, we can't have a carry }
|
|
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
|
|
TEST BL,BL
|
|
JNE @@digLoop
|
|
|
|
@@endDigits:
|
|
DEC CH
|
|
JE @@negate
|
|
TEST EAX,EAX
|
|
JGE @@successExit
|
|
JMP @@overFlow
|
|
|
|
@@empty:
|
|
INC ESI
|
|
JMP @@error
|
|
|
|
@@negate:
|
|
NEG EAX
|
|
JLE @@successExit
|
|
JS @@successExit { to handle 2**31 correctly, where the negate overflows }
|
|
|
|
@@error:
|
|
@@overFlow:
|
|
POP EBX
|
|
SUB ESI,EBX
|
|
JMP @@exit
|
|
|
|
@@minus:
|
|
INC CH
|
|
@@plus:
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
JMP @@firstDigit
|
|
|
|
@@dollar:
|
|
MOV EDI,0FFFFFFFH
|
|
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
TEST BL,BL
|
|
JZ @@empty
|
|
|
|
@@hDigLoop:
|
|
CMP BL,'a'
|
|
JB @@upper
|
|
SUB BL,'a' - 'A'
|
|
@@upper:
|
|
SUB BL,'0'
|
|
CMP BL,9
|
|
JBE @@digOk
|
|
SUB BL,'A' - '0'
|
|
CMP BL,5
|
|
JA @@error
|
|
ADD BL,10
|
|
@@digOk:
|
|
CMP EAX,EDI
|
|
JA @@overFlow
|
|
SHL EAX,4
|
|
ADD EAX,EBX
|
|
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
|
|
TEST BL,BL
|
|
JNE @@hDigLoop
|
|
|
|
@@successExit:
|
|
POP ECX { saved copy of string pointer }
|
|
XOR ESI,ESI { signal no error to caller }
|
|
|
|
@@exit:
|
|
MOV [EDX],ESI
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _WriteRec(var f: TFileRec; buffer: Pointer): Pointer;
|
|
{$IFDEF LINUX}
|
|
var
|
|
Dummy: Integer;
|
|
begin
|
|
_BlockWrite(f, Buffer, 1, Dummy);
|
|
Result := @F;
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
// -> EAX Pointer to file variable
|
|
// EDX Pointer to buffer
|
|
// <- EAX Pointer to file variable
|
|
PUSH EBX
|
|
|
|
MOV EBX,EAX
|
|
|
|
MOVZX EAX,[EAX].TFileRec.Mode
|
|
SUB EAX,fmOutput
|
|
CMP EAX,fmInOut-fmOutput // File must be fmInOut or fmOutput
|
|
JA @@fileNotOpen
|
|
|
|
// WriteFile(f.Handle, buffer, f.RecSize, @result, Nil);
|
|
|
|
PUSH 0 // space for OS result
|
|
MOV EAX,ESP
|
|
|
|
PUSH 0 // pass lpOverlapped
|
|
PUSH EAX // pass @result
|
|
PUSH [EBX].TFileRec.RecSize // pass nNumberOfBytesToRead
|
|
PUSH EDX // pass lpBuffer
|
|
PUSH [EBX].TFileRec.Handle // pass hFile
|
|
CALL WriteFile
|
|
POP EDX // pop result
|
|
DEC EAX // check EAX = TRUE
|
|
JNZ @@error
|
|
|
|
CMP EDX,[EBX].TFileRec.RecSize // result = f.RecSize ?
|
|
JE @@exit
|
|
|
|
@@writeError:
|
|
MOV EAX,101
|
|
JMP @@errExit
|
|
|
|
@@fileNotOpen:
|
|
MOV EAX,5
|
|
JMP @@errExit
|
|
|
|
@@error:
|
|
CALL GetLastError
|
|
@@errExit:
|
|
CALL SetInOutRes
|
|
@@exit:
|
|
MOV EAX,EBX
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
// If the file is Output or ErrOutput std variable, try to open it
|
|
// Otherwise, runtime error.
|
|
function TryOpenForOutput(var t: TTextRec): Boolean;
|
|
begin
|
|
if (@t = @Output) or (@t = @ErrOutput) then
|
|
begin
|
|
t.Flags := tfCRLF * Byte(DefaultTextLineBreakStyle);
|
|
_RewritText(t);
|
|
end;
|
|
|
|
Result := t.Mode = fmOutput;
|
|
if not Result then
|
|
SetInOutRes(105);
|
|
end;
|
|
|
|
function _WriteBytes(var t: TTextRec; const b; cnt : Longint): Pointer;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
P: PChar;
|
|
RemainingBytes: Longint;
|
|
Temp: Integer;
|
|
begin
|
|
Result := @t;
|
|
if t.Mode <> fmOutput and not TryOpenForOutput(t) then Exit;
|
|
|
|
P := t.BufPtr + t.BufPos;
|
|
RemainingBytes := t.BufSize - t.BufPos;
|
|
while RemainingBytes <= cnt do
|
|
begin
|
|
Inc(t.BufPos, RemainingBytes);
|
|
Dec(cnt, RemainingBytes);
|
|
Move(B, P^, RemainingBytes);
|
|
Temp := TTextIOFunc(t.InOutFunc)(t);
|
|
if Temp <> 0 then
|
|
begin
|
|
SetInOutRes(Temp);
|
|
Exit;
|
|
end;
|
|
P := t.BufPtr + t.BufPos;
|
|
RemainingBytes := t.BufSize - t.BufPos;
|
|
end;
|
|
Inc(t.BufPos, cnt);
|
|
Move(B, P^, cnt);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
// -> EAX Pointer to file record
|
|
// EDX Pointer to buffer
|
|
// ECX Number of bytes to write
|
|
// <- EAX Pointer to file record
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EDX
|
|
|
|
CMP [EAX].TTextRec.Mode,fmOutput
|
|
JE @@loop
|
|
PUSH EAX
|
|
PUSH EDX
|
|
PUSH ECX
|
|
CALL TryOpenForOutput
|
|
TEST AL,AL
|
|
POP ECX
|
|
POP EDX
|
|
POP EAX
|
|
JE @@exit
|
|
|
|
@@loop:
|
|
MOV EDI,[EAX].TTextRec.BufPtr
|
|
ADD EDI,[EAX].TTextRec.BufPos
|
|
|
|
// remainingBytes = t.bufSize - t.bufPos
|
|
|
|
MOV EDX,[EAX].TTextRec.BufSize
|
|
SUB EDX,[EAX].TTextRec.BufPos
|
|
|
|
// if (remainingBytes <= cnt)
|
|
|
|
CMP EDX,ECX
|
|
JG @@1
|
|
|
|
// t.BufPos += remainingBytes, cnt -= remainingBytes
|
|
|
|
ADD [EAX].TTextRec.BufPos,EDX
|
|
SUB ECX,EDX
|
|
|
|
// copy remainingBytes, advancing ESI
|
|
|
|
PUSH EAX
|
|
PUSH ECX
|
|
MOV ECX,EDX
|
|
REP MOVSB
|
|
|
|
CALL [EAX].TTextRec.InOutFunc
|
|
TEST EAX,EAX
|
|
JNZ @@error
|
|
|
|
POP ECX
|
|
POP EAX
|
|
JMP @@loop
|
|
|
|
@@error:
|
|
CALL SetInOutRes
|
|
POP ECX
|
|
POP EAX
|
|
JMP @@exit
|
|
@@1:
|
|
ADD [EAX].TTextRec.BufPos,ECX
|
|
REP MOVSB
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _WriteSpaces(var t: TTextRec; cnt: Longint): Pointer;
|
|
{$IFDEF PUREPASCAL}
|
|
const
|
|
s64Spaces = ' ';
|
|
begin
|
|
Result := @t;
|
|
while cnt > 64 do
|
|
begin
|
|
_WriteBytes(t, s64Spaces, 64);
|
|
if InOutRes <> 0 then Exit;
|
|
Dec(cnt, 64);
|
|
end;
|
|
if cnt > 0 then
|
|
_WriteBytes(t, s64Spaces, cnt);
|
|
end;
|
|
{$ELSE}
|
|
const
|
|
spCnt = 64;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// EDX Number of spaces (<= 0: None)
|
|
|
|
MOV ECX,EDX
|
|
@@loop:
|
|
{$IFDEF PIC}
|
|
LEA EDX, [EBX] + offset @@spBuf
|
|
{$ELSE}
|
|
MOV EDX,offset @@spBuf
|
|
{$ENDIF}
|
|
|
|
CMP ECX,spCnt
|
|
JLE @@1
|
|
|
|
SUB ECX,spCnt
|
|
PUSH EAX
|
|
PUSH ECX
|
|
MOV ECX,spCnt
|
|
CALL _WriteBytes
|
|
CALL SysInit.@GetTLS
|
|
CMP [EAX].InOutRes,0
|
|
JNE @@error
|
|
POP ECX
|
|
POP EAX
|
|
JMP @@loop
|
|
|
|
@@error:
|
|
POP ECX
|
|
POP EAX
|
|
JMP @@exit
|
|
|
|
@@spBuf: // 64 spaces
|
|
DB ' ';
|
|
@@1:
|
|
TEST ECX,ECX
|
|
JG _WriteBytes
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _Write0Char(var t: TTextRec; c: Char): Pointer;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
Temp: Integer;
|
|
begin
|
|
Result := @t;
|
|
if not TryOpenForOutput(t) then Exit;
|
|
|
|
if t.BufPos >= t.BufSize then
|
|
begin
|
|
Temp := TTextIOFunc(t.InOutFunc)(t);
|
|
if Temp <> 0 then
|
|
begin
|
|
SetInOutRes(Temp);
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
t.BufPtr[t.BufPos] := c;
|
|
Inc(t.BufPos);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// DL Character
|
|
|
|
CMP [EAX].TTextRec.Mode,fmOutput
|
|
JE @@loop
|
|
PUSH EAX
|
|
PUSH EDX
|
|
CALL TryOpenForOutput
|
|
TEST AL,AL
|
|
POP EDX
|
|
POP EAX
|
|
JNE @@loop
|
|
JMP @@exit
|
|
|
|
@@flush:
|
|
PUSH EAX
|
|
PUSH EDX
|
|
CALL [EAX].TTextRec.InOutFunc
|
|
TEST EAX,EAX
|
|
JNZ @@error
|
|
POP EDX
|
|
POP EAX
|
|
JMP @@loop
|
|
|
|
@@error:
|
|
CALL SetInOutRes
|
|
POP EDX
|
|
POP EAX
|
|
JMP @@exit
|
|
|
|
@@loop:
|
|
MOV ECX,[EAX].TTextRec.BufPos
|
|
CMP ECX,[EAX].TTextRec.BufSize
|
|
JGE @@flush
|
|
|
|
ADD ECX,[EAX].TTextRec.BufPtr
|
|
MOV [ECX],DL
|
|
|
|
INC [EAX].TTextRec.BufPos
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _WriteChar(var t: TTextRec; c: Char; width: Integer): Pointer;
|
|
begin
|
|
_WriteSpaces(t, width-1);
|
|
Result := _WriteBytes(t, c, 1);
|
|
end;
|
|
|
|
function _WriteBool(var t: TTextRec; val: Boolean; width: Longint): Pointer;
|
|
const
|
|
BoolStrs: array [Boolean] of ShortString = ('FALSE', 'TRUE');
|
|
begin
|
|
Result := _WriteString(t, BoolStrs[val], width);
|
|
end;
|
|
|
|
function _Write0Bool(var t: TTextRec; val: Boolean): Pointer;
|
|
begin
|
|
Result := _WriteBool(t, val, 0);
|
|
end;
|
|
|
|
function _WriteLong(var t: TTextRec; val, width: Longint): Pointer;
|
|
var
|
|
S: string[31];
|
|
begin
|
|
Str(val:0, S);
|
|
Result := _WriteString(t, S, width);
|
|
end;
|
|
|
|
function _Write0Long(var t: TTextRec; val: Longint): Pointer;
|
|
begin
|
|
Result := _WriteLong(t, val, 0);
|
|
end;
|
|
|
|
function _Write0String(var t: TTextRec; const s: ShortString): Pointer;
|
|
begin
|
|
Result := _WriteBytes(t, S[1], Byte(S[0]));
|
|
end;
|
|
|
|
function _WriteString(var t: TTextRec; const s: ShortString; width: Longint): Pointer;
|
|
begin
|
|
_WriteSpaces(t, Width - Byte(S[0]));
|
|
Result := _WriteBytes(t, S[1], Byte(S[0]));
|
|
end;
|
|
|
|
function _Write0CString(var t: TTextRec; s: PChar): Pointer;
|
|
begin
|
|
Result := _WriteCString(t, s, 0);
|
|
end;
|
|
|
|
function _WriteCString(var t: TTextRec; s: PChar; width: Longint): Pointer;
|
|
var
|
|
len: Longint;
|
|
begin
|
|
len := _strlen(s);
|
|
_WriteSpaces(t, width - len);
|
|
Result := _WriteBytes(t, s^, len);
|
|
end;
|
|
|
|
procedure _Write2Ext;
|
|
asm
|
|
{ PROCEDURE _Write2Ext( VAR t: Text; val: Extended; width, prec: Longint);
|
|
->EAX Pointer to file record
|
|
[ESP+4] Extended value
|
|
EDX Field width
|
|
ECX precision (<0: scientific, >= 0: fixed point) }
|
|
|
|
FLD tbyte ptr [ESP+4] { load value }
|
|
SUB ESP,256 { VAR s: String; }
|
|
|
|
PUSH EAX
|
|
PUSH EDX
|
|
|
|
{ Str( val, width, prec, s ); }
|
|
|
|
SUB ESP,12
|
|
FSTP tbyte ptr [ESP] { pass value }
|
|
MOV EAX,EDX { pass field width }
|
|
MOV EDX,ECX { pass precision }
|
|
LEA ECX,[ESP+8+12] { pass destination string }
|
|
CALL _Str2Ext
|
|
|
|
{ Write( t, s, width ); }
|
|
|
|
POP ECX { pass width }
|
|
POP EAX { pass text }
|
|
MOV EDX,ESP { pass string }
|
|
CALL _WriteString
|
|
|
|
ADD ESP,256
|
|
RET 12
|
|
end;
|
|
|
|
procedure _Write1Ext;
|
|
asm
|
|
{ PROCEDURE _Write1Ext( VAR t: Text; val: Extended; width: Longint);
|
|
-> EAX Pointer to file record
|
|
[ESP+4] Extended value
|
|
EDX Field width }
|
|
|
|
OR ECX,-1
|
|
JMP _Write2Ext
|
|
end;
|
|
|
|
procedure _Write0Ext;
|
|
asm
|
|
{ PROCEDURE _Write0Ext( VAR t: Text; val: Extended);
|
|
->EAX Pointer to file record
|
|
[ESP+4] Extended value }
|
|
|
|
MOV EDX,23 { field width }
|
|
OR ECX,-1
|
|
JMP _Write2Ext
|
|
end;
|
|
|
|
function _WriteLn(var t: TTextRec): Pointer;
|
|
var
|
|
Buf: array [0..1] of Char;
|
|
begin
|
|
if (t.flags and tfCRLF) <> 0 then
|
|
begin
|
|
Buf[0] := #13;
|
|
Buf[1] := #10;
|
|
Result := _WriteBytes(t, Buf, 2);
|
|
end
|
|
else
|
|
begin
|
|
Buf[0] := #10;
|
|
Result := _WriteBytes(t, Buf, 1);
|
|
end;
|
|
_Flush(t);
|
|
end;
|
|
|
|
procedure __CToPasStr(Dest: PShortString; const Source: PChar);
|
|
begin
|
|
__CLenToPasStr(Dest, Source, 255);
|
|
end;
|
|
|
|
procedure __CLenToPasStr(Dest: PShortString; const Source: PChar; MaxLen: Integer);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
I: Integer;
|
|
begin
|
|
I := 0;
|
|
if MaxLen > 255 then MaxLen := 255;
|
|
while (Source[I] <> #0) and (I <= MaxLen) do
|
|
begin
|
|
Dest^[I+1] := Source[I];
|
|
Inc(I);
|
|
end;
|
|
if I > 0 then Dec(I);
|
|
Byte(Dest^[0]) := I;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ ->EAX Pointer to destination }
|
|
{ EDX Pointer to source }
|
|
{ ECX cnt }
|
|
|
|
PUSH EBX
|
|
PUSH EAX { save destination }
|
|
|
|
CMP ECX,255
|
|
JBE @@loop
|
|
MOV ECX,255
|
|
@@loop:
|
|
MOV BL,[EDX] { ch = *src++; }
|
|
INC EDX
|
|
TEST BL,BL { if (ch == 0) break }
|
|
JE @@endLoop
|
|
INC EAX { *++dest = ch; }
|
|
MOV [EAX],BL
|
|
DEC ECX { while (--cnt != 0) }
|
|
JNZ @@loop
|
|
|
|
@@endLoop:
|
|
POP EDX
|
|
SUB EAX,EDX
|
|
MOV [EDX],AL
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure __ArrayToPasStr(Dest: PShortString; const Source: PChar; Len: Integer);
|
|
begin
|
|
if Len > 255 then Len := 255;
|
|
Byte(Dest^[0]) := Len;
|
|
Move(Source^, Dest^[1], Len);
|
|
end;
|
|
|
|
procedure __PasToCStr(const Source: PShortString; const Dest: PChar);
|
|
begin
|
|
Move(Source^[1], Dest^, Byte(Source^[0]));
|
|
Dest[Byte(Source^[0])] := #0;
|
|
end;
|
|
|
|
procedure _SetElem;
|
|
asm
|
|
{ PROCEDURE _SetElem( VAR d: SET; elem, size: Byte); }
|
|
{ EAX = dest address }
|
|
{ DL = element number }
|
|
{ CL = size of set }
|
|
|
|
PUSH EBX
|
|
PUSH EDI
|
|
|
|
MOV EDI,EAX
|
|
|
|
XOR EBX,EBX { zero extend set size into ebx }
|
|
MOV BL,CL
|
|
MOV ECX,EBX { and use it for the fill }
|
|
|
|
XOR EAX,EAX { for zero fill }
|
|
REP STOSB
|
|
|
|
SUB EDI,EBX { point edi at beginning of set again }
|
|
|
|
INC EAX { eax is still zero - make it 1 }
|
|
MOV CL,DL
|
|
ROL AL,CL { generate a mask }
|
|
SHR ECX,3 { generate the index }
|
|
CMP ECX,EBX { if index >= siz then exit }
|
|
JAE @@exit
|
|
OR [EDI+ECX],AL{ set bit }
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _SetRange;
|
|
asm
|
|
{ PROCEDURE _SetRange( lo, hi, size: Byte; VAR d: SET ); }
|
|
{ ->AL low limit of range }
|
|
{ DL high limit of range }
|
|
{ ECX Pointer to set }
|
|
{ AH size of set }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
XOR EBX,EBX { EBX = set size }
|
|
MOV BL,AH
|
|
MOVZX ESI,AL { ESI = low zero extended }
|
|
MOVZX EDX,DL { EDX = high zero extended }
|
|
MOV EDI,ECX
|
|
|
|
{ clear the set }
|
|
|
|
MOV ECX,EBX
|
|
XOR EAX,EAX
|
|
REP STOSB
|
|
|
|
{ prepare for setting the bits }
|
|
|
|
SUB EDI,EBX { point EDI at start of set }
|
|
SHL EBX,3 { EBX = highest bit in set + 1 }
|
|
CMP EDX,EBX
|
|
JB @@inrange
|
|
LEA EDX,[EBX-1] { ECX = highest bit in set }
|
|
|
|
@@inrange:
|
|
CMP ESI,EDX { if lo > hi then exit; }
|
|
JA @@exit
|
|
|
|
DEC EAX { loMask = 0xff << (lo & 7) }
|
|
MOV ECX,ESI
|
|
AND CL,07H
|
|
SHL AL,CL
|
|
|
|
SHR ESI,3 { loIndex = lo >> 3; }
|
|
|
|
MOV CL,DL { hiMask = 0xff >> (7 - (hi & 7)); }
|
|
NOT CL
|
|
AND CL,07
|
|
SHR AH,CL
|
|
|
|
SHR EDX,3 { hiIndex = hi >> 3; }
|
|
|
|
ADD EDI,ESI { point EDI to set[loIndex] }
|
|
MOV ECX,EDX
|
|
SUB ECX,ESI { if ((inxDiff = (hiIndex - loIndex)) == 0) }
|
|
JNE @@else
|
|
|
|
AND AL,AH { set[loIndex] = hiMask & loMask; }
|
|
MOV [EDI],AL
|
|
JMP @@exit
|
|
|
|
@@else:
|
|
STOSB { set[loIndex++] = loMask; }
|
|
DEC ECX
|
|
MOV AL,0FFH { while (loIndex < hiIndex) }
|
|
REP STOSB { set[loIndex++] = 0xff; }
|
|
MOV [EDI],AH { set[hiIndex] = hiMask; }
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _SetEq;
|
|
asm
|
|
{ FUNCTION _SetEq( CONST l, r: Set; size: Byte): ConditionCode; }
|
|
{ EAX = left operand }
|
|
{ EDX = right operand }
|
|
{ CL = size of set }
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
|
|
AND ECX,0FFH
|
|
REP CMPSB
|
|
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
procedure _SetLe;
|
|
asm
|
|
{ FUNCTION _SetLe( CONST l, r: Set; size: Byte): ConditionCode; }
|
|
{ EAX = left operand }
|
|
{ EDX = right operand }
|
|
{ CL = size of set (>0 && <= 32) }
|
|
|
|
@@loop:
|
|
MOV CH,[EDX]
|
|
NOT CH
|
|
AND CH,[EAX]
|
|
JNE @@exit
|
|
INC EDX
|
|
INC EAX
|
|
DEC CL
|
|
JNZ @@loop
|
|
@@exit:
|
|
end;
|
|
|
|
procedure _SetIntersect;
|
|
asm
|
|
{ PROCEDURE _SetIntersect( VAR dest: Set; CONST src: Set; size: Byte);}
|
|
{ EAX = destination operand }
|
|
{ EDX = source operand }
|
|
{ CL = size of set (0 < size <= 32) }
|
|
|
|
@@loop:
|
|
MOV CH,[EDX]
|
|
INC EDX
|
|
AND [EAX],CH
|
|
INC EAX
|
|
DEC CL
|
|
JNZ @@loop
|
|
end;
|
|
|
|
procedure _SetIntersect3;
|
|
asm
|
|
{ PROCEDURE _SetIntersect3( VAR dest: Set; CONST src: Set; size: Longint; src2: Set);}
|
|
{ EAX = destination operand }
|
|
{ EDX = source operand }
|
|
{ ECX = size of set (0 < size <= 32) }
|
|
{ [ESP+4] = 2nd source operand }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV ESI,[ESP+8+4]
|
|
@@loop:
|
|
MOV BL,[EDX+ECX-1]
|
|
AND BL,[ESI+ECX-1]
|
|
MOV [EAX+ECX-1],BL
|
|
DEC ECX
|
|
JNZ @@loop
|
|
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _SetUnion;
|
|
asm
|
|
{ PROCEDURE _SetUnion( VAR dest: Set; CONST src: Set; size: Byte); }
|
|
{ EAX = destination operand }
|
|
{ EDX = source operand }
|
|
{ CL = size of set (0 < size <= 32) }
|
|
|
|
@@loop:
|
|
MOV CH,[EDX]
|
|
INC EDX
|
|
OR [EAX],CH
|
|
INC EAX
|
|
DEC CL
|
|
JNZ @@loop
|
|
end;
|
|
|
|
procedure _SetUnion3;
|
|
asm
|
|
{ PROCEDURE _SetUnion3( VAR dest: Set; CONST src: Set; size: Longint; src2: Set);}
|
|
{ EAX = destination operand }
|
|
{ EDX = source operand }
|
|
{ ECX = size of set (0 < size <= 32) }
|
|
{ [ESP+4] = 2nd source operand }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV ESI,[ESP+8+4]
|
|
@@loop:
|
|
MOV BL,[EDX+ECX-1]
|
|
OR BL,[ESI+ECX-1]
|
|
MOV [EAX+ECX-1],BL
|
|
DEC ECX
|
|
JNZ @@loop
|
|
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _SetSub;
|
|
asm
|
|
{ PROCEDURE _SetSub( VAR dest: Set; CONST src: Set; size: Byte); }
|
|
{ EAX = destination operand }
|
|
{ EDX = source operand }
|
|
{ CL = size of set (0 < size <= 32) }
|
|
|
|
@@loop:
|
|
MOV CH,[EDX]
|
|
NOT CH
|
|
INC EDX
|
|
AND [EAX],CH
|
|
INC EAX
|
|
DEC CL
|
|
JNZ @@loop
|
|
end;
|
|
|
|
procedure _SetSub3;
|
|
asm
|
|
{ PROCEDURE _SetSub3( VAR dest: Set; CONST src: Set; size: Longint; src2: Set);}
|
|
{ EAX = destination operand }
|
|
{ EDX = source operand }
|
|
{ ECX = size of set (0 < size <= 32) }
|
|
{ [ESP+4] = 2nd source operand }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV ESI,[ESP+8+4]
|
|
@@loop:
|
|
MOV BL,[ESI+ECX-1]
|
|
NOT BL
|
|
AND BL,[EDX+ECX-1]
|
|
MOV [EAX+ECX-1],BL
|
|
DEC ECX
|
|
JNZ @@loop
|
|
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _SetExpand;
|
|
asm
|
|
{ PROCEDURE _SetExpand( CONST src: Set; VAR dest: Set; lo, hi: Byte); }
|
|
{ ->EAX Pointer to source (packed set) }
|
|
{ EDX Pointer to destination (expanded set) }
|
|
{ CH high byte of source }
|
|
{ CL low byte of source }
|
|
|
|
{ algorithm: }
|
|
{ clear low bytes }
|
|
{ copy high-low+1 bytes }
|
|
{ clear 31-high bytes }
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
|
|
MOV EDX,ECX { save low, high in dl, dh }
|
|
XOR ECX,ECX
|
|
XOR EAX,EAX
|
|
|
|
MOV CL,DL { clear low bytes }
|
|
REP STOSB
|
|
|
|
MOV CL,DH { copy high - low bytes }
|
|
SUB CL,DL
|
|
REP MOVSB
|
|
|
|
MOV CL,32 { copy 32 - high bytes }
|
|
SUB CL,DH
|
|
REP STOSB
|
|
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
procedure _EmitDigits;
|
|
const
|
|
tenE17: Double = 1e17;
|
|
tenE18: Double = 1e18;
|
|
asm
|
|
// -> FST(0) Value, 0 <= value < 10.0
|
|
// EAX Count of digits to generate
|
|
// EDX Pointer to digit buffer
|
|
|
|
PUSH EBX
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
CALL GetGOT
|
|
MOV EBX,EAX
|
|
POP EAX
|
|
{$ELSE}
|
|
XOR EBX,EBX
|
|
{$ENDIF}
|
|
PUSH EDI
|
|
MOV EDI,EDX
|
|
MOV ECX,EAX
|
|
|
|
SUB ESP,10 // VAR bcdBuf: array [0..9] of Byte
|
|
MOV byte ptr [EDI],'0' // digBuf[0] := '0'//
|
|
FMUL qword ptr [EBX] + offset tenE17 // val := Round(val*1e17);
|
|
FRNDINT
|
|
|
|
FCOM qword ptr [EBX] + offset tenE18 // if val >= 1e18 then
|
|
FSTSW AX
|
|
SAHF
|
|
JB @@1
|
|
|
|
FSUB qword ptr [EBX] + offset tenE18 // val := val - 1e18;
|
|
MOV byte ptr [EDI],'1' // digBuf[0] := '1';
|
|
@@1:
|
|
FBSTP tbyte ptr [ESP] // store packed bcd digits in bcdBuf
|
|
|
|
MOV EDX,8
|
|
INC EDI
|
|
|
|
@@2:
|
|
WAIT
|
|
MOV AL,[ESP+EDX] // unpack 18 bcd digits in 9 bytes
|
|
MOV AH,AL // into 9 words = 18 bytes
|
|
SHR AL,4
|
|
AND AH,0FH
|
|
ADD AX,'00'
|
|
STOSW
|
|
DEC EDX
|
|
JNS @@2
|
|
|
|
SUB ECX,18 // we need at least digCnt digits
|
|
JL @@3 // we have generated 18
|
|
|
|
MOV AL,'0' // if this is not enough, append zeroes
|
|
REP STOSB
|
|
JMP @@4 // in this case, we don't need to round
|
|
|
|
@@3:
|
|
ADD EDI,ECX // point EDI to the round digit
|
|
CMP byte ptr [EDI],'5'
|
|
JL @@4
|
|
@@5:
|
|
DEC EDI
|
|
INC byte ptr [EDI]
|
|
CMP byte ptr [EDI],'9'
|
|
JLE @@4
|
|
MOV byte ptr [EDI],'0'
|
|
JMP @@5
|
|
|
|
@@4:
|
|
ADD ESP,10
|
|
POP EDI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _ScaleExt;
|
|
asm
|
|
// -> FST(0) Value
|
|
// <- EAX exponent (base 10)
|
|
// FST(0) Value / 10**eax
|
|
// PIC safe - uses EBX, but only call is to _POW10, which fixes up EBX itself
|
|
|
|
PUSH EBX
|
|
SUB ESP,12
|
|
|
|
XOR EBX,EBX
|
|
|
|
@@normLoop: // loop necessary for denormals
|
|
|
|
FLD ST(0)
|
|
FSTP tbyte ptr [ESP]
|
|
MOV AX,[ESP+8]
|
|
TEST AX,AX
|
|
JE @@testZero
|
|
@@cont:
|
|
SUB AX,3FFFH
|
|
MOV DX,4D10H // log10(2) * 2**16
|
|
IMUL DX
|
|
MOVSX EAX,DX // exp10 = exp2 * log10(2)
|
|
NEG EAX
|
|
JE @@exit
|
|
SUB EBX,EAX
|
|
CALL _Pow10
|
|
JMP @@normLoop
|
|
|
|
@@testZero:
|
|
CMP dword ptr [ESP+4],0
|
|
JNE @@cont
|
|
CMP dword ptr [ESP+0],0
|
|
JNE @@cont
|
|
|
|
@@exit:
|
|
ADD ESP,12
|
|
MOV EAX,EBX
|
|
POP EBX
|
|
end;
|
|
|
|
const
|
|
Ten: Double = 10.0;
|
|
NanStr: String[3] = 'Nan';
|
|
PlusInfStr: String[4] = '+Inf';
|
|
MinInfStr: String[4] = '-Inf';
|
|
|
|
procedure _Str2Ext;//( val: Extended; width, precision: Longint; var s: String );
|
|
const
|
|
MAXDIGS = 256;
|
|
asm
|
|
// -> [ESP+4] Extended value
|
|
// EAX Width
|
|
// EDX Precision
|
|
// ECX Pointer to string
|
|
|
|
FLD tbyte ptr [ESP+4]
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
PUSH ECX // save string pointer
|
|
SUB ESP,MAXDIGS // VAR digBuf: array [0..MAXDIGS-1] of Char
|
|
|
|
// limit width to 255
|
|
|
|
CMP EBX,255 // if width > 255 then width := 255;
|
|
JLE @@1
|
|
MOV EBX,255
|
|
@@1:
|
|
|
|
// save sign bit in bit 0 of EDI, take absolute value of val, check for
|
|
// Nan and infinity.
|
|
|
|
FLD ST(0)
|
|
FSTP tbyte ptr [ESP]
|
|
XOR EAX,EAX
|
|
MOV AX,word ptr [ESP+8]
|
|
MOV EDI,EAX
|
|
SHR EDI,15
|
|
AND AX,7FFFH
|
|
CMP AX,7FFFH
|
|
JE @@nanInf
|
|
FABS
|
|
|
|
// if precision < 0 then do scientific else do fixed;
|
|
|
|
TEST ESI,ESI
|
|
JGE @@fixed
|
|
|
|
// the following call finds a decimal exponent and a reduced
|
|
// mantissa such that val = mant * 10**exp
|
|
|
|
CALL _ScaleExt // val is FST(0), exp is EAX
|
|
|
|
// for scientific notation, we have width - 8 significant digits
|
|
// however, we can not have less than 2 or more than 18 digits.
|
|
|
|
@@scientific:
|
|
|
|
MOV ESI,EBX // digCnt := width - 8;
|
|
SUB ESI,8
|
|
CMP ESI,2 // if digCnt < 2 then digCnt := 2
|
|
JGE @@2
|
|
MOV ESI,2
|
|
JMP @@3
|
|
@@2:
|
|
CMP ESI,18 // else if digCnt > 18 then digCnt := 18;
|
|
JLE @@3
|
|
MOV ESI,18
|
|
@@3:
|
|
|
|
// _EmitDigits( val, digCnt, digBuf )
|
|
|
|
MOV EDX,ESP // pass digBuf
|
|
PUSH EAX // save exponent
|
|
MOV EAX,ESI // pass digCnt
|
|
CALL _EmitDigits // convert val to ASCII
|
|
|
|
MOV EDX,EDI // save sign in EDX
|
|
MOV EDI,[ESP+MAXDIGS+4] // load result string pointer
|
|
|
|
MOV [EDI],BL // length of result string := width
|
|
INC EDI
|
|
|
|
MOV AL,' ' // prepare for leading blanks and sign
|
|
|
|
MOV ECX,EBX // blankCnt := width - digCnt - 8
|
|
SUB ECX,ESI
|
|
SUB ECX,8
|
|
JLE @@4
|
|
|
|
REP STOSB // emit blankCnt blanks
|
|
@@4:
|
|
SUB [EDI-1],CL // if blankCnt < 0, adjust length
|
|
|
|
TEST DL,DL // emit the sign (' ' or '-')
|
|
JE @@5
|
|
MOV AL,'-'
|
|
@@5:
|
|
STOSB
|
|
|
|
POP EAX
|
|
MOV ECX,ESI // emit digCnt digits
|
|
MOV ESI,ESP // point ESI to digBuf
|
|
|
|
CMP byte ptr [ESI],'0'
|
|
JE @@5a // if rounding overflowed, adjust exponent and ESI
|
|
INC EAX
|
|
DEC ESI
|
|
@@5a:
|
|
INC ESI
|
|
|
|
MOVSB // emit one digit
|
|
MOV byte ptr [EDI],'.' // emit dot
|
|
INC EDI // adjust dest pointer
|
|
DEC ECX // adjust count
|
|
|
|
REP MOVSB
|
|
|
|
MOV byte ptr [EDI],'E'
|
|
|
|
MOV CL,'+' // emit sign of exponent ('+' or '-')
|
|
TEST EAX,EAX
|
|
JGE @@6
|
|
MOV CL,'-'
|
|
NEG EAX
|
|
@@6:
|
|
MOV [EDI+1],CL
|
|
|
|
XOR EDX,EDX // emit exponent
|
|
MOV CX,10
|
|
DIV CX
|
|
ADD DL,'0'
|
|
MOV [EDI+5],DL
|
|
|
|
XOR EDX,EDX
|
|
DIV CX
|
|
ADD DL,'0'
|
|
MOV [EDI+4],DL
|
|
|
|
XOR EDX,EDX
|
|
DIV CX
|
|
ADD DL,'0'
|
|
MOV [EDI+3],DL
|
|
|
|
ADD AL,'0'
|
|
MOV [EDI+2],AL
|
|
|
|
JMP @@exit
|
|
|
|
@@fixed:
|
|
|
|
// FST(0) = value >= 0.0
|
|
// EBX = width
|
|
// ESI = precision
|
|
// EDI = sign
|
|
|
|
CMP ESI,MAXDIGS-40 // else if precision > MAXDIGS-40 then precision := MAXDIGS-40;
|
|
JLE @@6a
|
|
MOV ESI,MAXDIGS-40
|
|
@@6a:
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
CALL GetGOT
|
|
FCOM qword ptr [EAX] + offset Ten
|
|
POP EAX
|
|
{$ELSE}
|
|
FCOM qword ptr ten
|
|
{$ENDIF}
|
|
FSTSW AX
|
|
SAHF
|
|
MOV EAX,0
|
|
JB @@7
|
|
|
|
CALL _ScaleExt // val is FST(0), exp is EAX
|
|
|
|
CMP EAX,35 // if val is too large, use scientific
|
|
JG @@scientific
|
|
|
|
@@7:
|
|
// FST(0) = scaled value, 0.0 <= value < 10.0
|
|
// EAX = exponent, 0 <= exponent
|
|
|
|
// intDigCnt := exponent + 1;
|
|
|
|
INC EAX
|
|
|
|
// _EmitDigits( value, intDigCnt + precision, digBuf );
|
|
|
|
MOV EDX,ESP
|
|
PUSH EAX
|
|
ADD EAX,ESI
|
|
CALL _EmitDigits
|
|
POP EAX
|
|
|
|
// Now we need to check whether rounding to the right number of
|
|
// digits overflowed, and if so, adjust things accordingly
|
|
|
|
MOV EDX,ESI // put precision in EDX
|
|
MOV ESI,ESP // point EDI to digBuf
|
|
CMP byte ptr [ESI],'0'
|
|
JE @@8
|
|
INC EAX
|
|
DEC ESI
|
|
@@8:
|
|
INC ESI
|
|
|
|
MOV ECX,EAX // numWidth := sign + intDigCnt;
|
|
ADD ECX,EDI
|
|
|
|
TEST EDX,EDX // if precision > 0 then
|
|
JE @@9
|
|
INC ECX // numWidth := numWidth + 1 + precision
|
|
ADD ECX,EDX
|
|
|
|
CMP EBX,ECX // if width <= numWidth
|
|
JG @@9
|
|
MOV EBX,ECX // width := numWidth
|
|
@@9:
|
|
PUSH EAX
|
|
PUSH EDI
|
|
|
|
MOV EDI,[ESP+MAXDIGS+2*4] // point EDI to dest string
|
|
|
|
MOV [EDI],BL // store final length in dest string
|
|
INC EDI
|
|
|
|
SUB EBX,ECX // width := width - numWidth
|
|
MOV ECX,EBX
|
|
JLE @@10
|
|
|
|
MOV AL,' ' // emit width blanks
|
|
REP STOSB
|
|
@@10:
|
|
SUB [EDI-1],CL // if blankCnt < 0, adjust length
|
|
POP EAX
|
|
POP ECX
|
|
|
|
TEST EAX,EAX
|
|
JE @@11
|
|
|
|
MOV byte ptr [EDI],'-'
|
|
INC EDI
|
|
|
|
@@11:
|
|
REP MOVSB // copy intDigCnt digits
|
|
|
|
TEST EDX,EDX // if precision > 0 then
|
|
JE @@12
|
|
|
|
MOV byte ptr [EDI],'.' // emit '.'
|
|
INC EDI
|
|
MOV ECX,EDX // emit precision digits
|
|
REP MOVSB
|
|
|
|
@@12:
|
|
|
|
@@exit:
|
|
ADD ESP,MAXDIGS
|
|
POP ECX
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
RET 12
|
|
|
|
@@nanInf:
|
|
// here: EBX = width, ECX = string pointer, EDI = sign, [ESP] = value
|
|
|
|
{$IFDEF PIC}
|
|
CALL GetGOT
|
|
{$ELSE}
|
|
XOR EAX,EAX
|
|
{$ENDIF}
|
|
FSTP ST(0)
|
|
CMP dword ptr [ESP+4],80000000H
|
|
LEA ESI,[EAX] + offset nanStr
|
|
JNE @@13
|
|
DEC EDI
|
|
LEA ESI,[EAX] + offset plusInfStr
|
|
JNZ @@13
|
|
LEA ESI,[EAX] + offset minInfStr
|
|
@@13:
|
|
MOV EDI,ECX
|
|
MOV ECX,EBX
|
|
MOV [EDI],CL
|
|
INC EDI
|
|
SUB CL,[ESI]
|
|
JBE @@14
|
|
MOV AL,' '
|
|
REP STOSB
|
|
@@14:
|
|
SUB [EDI-1],CL
|
|
MOV CL,[ESI]
|
|
INC ESI
|
|
REP MOVSB
|
|
|
|
JMP @@exit
|
|
end;
|
|
|
|
procedure _Str0Ext;
|
|
asm
|
|
// -> [ESP+4] Extended value
|
|
// EAX Pointer to string
|
|
|
|
MOV ECX,EAX // pass string
|
|
MOV EAX,23 // pass default field width
|
|
OR EDX,-1 // pass precision -1
|
|
JMP _Str2Ext
|
|
end;
|
|
|
|
procedure _Str1Ext;//( val: Extended; width: Longint; var s: String );
|
|
asm
|
|
// -> [ESP+4] Extended value
|
|
// EAX Field width
|
|
// EDX Pointer to string
|
|
|
|
MOV ECX,EDX
|
|
OR EDX,-1 // pass precision -1
|
|
JMP _Str2Ext
|
|
end;
|
|
|
|
//function _ValExt( s: AnsiString; VAR code: Integer ) : Extended;
|
|
procedure _ValExt;
|
|
asm
|
|
// -> EAX Pointer to string
|
|
// EDX Pointer to code result
|
|
// <- FST(0) Result
|
|
|
|
PUSH EBX
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
CALL GetGOT
|
|
MOV EBX,EAX
|
|
POP EAX
|
|
{$ELSE}
|
|
XOR EBX,EBX
|
|
{$ENDIF}
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
PUSH EBX // SaveGOT = ESP+8
|
|
MOV ESI,EAX
|
|
PUSH EAX // save for the error case
|
|
|
|
FLDZ
|
|
XOR EAX,EAX
|
|
XOR EBX,EBX
|
|
XOR EDI,EDI
|
|
|
|
PUSH EBX // temp to get digs to fpu
|
|
|
|
TEST ESI,ESI
|
|
JE @@empty
|
|
|
|
@@blankLoop:
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
CMP BL,' '
|
|
JE @@blankLoop
|
|
|
|
@@endBlanks:
|
|
MOV CH,0
|
|
CMP BL,'-'
|
|
JE @@minus
|
|
CMP BL,'+'
|
|
JE @@plus
|
|
JMP @@firstDigit
|
|
|
|
@@minus:
|
|
INC CH
|
|
@@plus:
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
|
|
@@firstDigit:
|
|
TEST BL,BL
|
|
JE @@error
|
|
|
|
MOV EDI,[ESP+8] // SaveGOT
|
|
|
|
@@digLoop:
|
|
SUB BL,'0'
|
|
CMP BL,9
|
|
JA @@dotExp
|
|
FMUL qword ptr [EDI] + offset Ten
|
|
MOV dword ptr [ESP],EBX
|
|
FIADD dword ptr [ESP]
|
|
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
|
|
TEST BL,BL
|
|
JNE @@digLoop
|
|
JMP @@prefinish
|
|
|
|
@@dotExp:
|
|
CMP BL,'.' - '0'
|
|
JNE @@exp
|
|
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
|
|
TEST BL,BL
|
|
JE @@prefinish
|
|
|
|
// EDI = SaveGot
|
|
@@fracDigLoop:
|
|
SUB BL,'0'
|
|
CMP BL,9
|
|
JA @@exp
|
|
FMUL qword ptr [EDI] + offset Ten
|
|
MOV dword ptr [ESP],EBX
|
|
FIADD dword ptr [ESP]
|
|
DEC EAX
|
|
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
|
|
TEST BL,BL
|
|
JNE @@fracDigLoop
|
|
|
|
@@prefinish:
|
|
XOR EDI,EDI
|
|
JMP @@finish
|
|
|
|
@@exp:
|
|
CMP BL,'E' - '0'
|
|
JE @@foundExp
|
|
CMP BL,'e' - '0'
|
|
JNE @@error
|
|
@@foundExp:
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
MOV AH,0
|
|
CMP BL,'-'
|
|
JE @@minusExp
|
|
CMP BL,'+'
|
|
JE @@plusExp
|
|
JMP @@firstExpDigit
|
|
@@minusExp:
|
|
INC AH
|
|
@@plusExp:
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
@@firstExpDigit:
|
|
SUB BL,'0'
|
|
CMP BL,9
|
|
JA @@error
|
|
MOV EDI,EBX
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
TEST BL,BL
|
|
JZ @@endExp
|
|
@@expDigLoop:
|
|
SUB BL,'0'
|
|
CMP BL,9
|
|
JA @@error
|
|
LEA EDI,[EDI+EDI*4]
|
|
ADD EDI,EDI
|
|
ADD EDI,EBX
|
|
MOV BL,[ESI]
|
|
INC ESI
|
|
TEST BL,BL
|
|
JNZ @@expDigLoop
|
|
@@endExp:
|
|
DEC AH
|
|
JNZ @@expPositive
|
|
NEG EDI
|
|
@@expPositive:
|
|
MOVSX EAX,AL
|
|
|
|
@@finish:
|
|
ADD EAX,EDI
|
|
PUSH EDX
|
|
PUSH ECX
|
|
CALL _Pow10
|
|
POP ECX
|
|
POP EDX
|
|
|
|
DEC CH
|
|
JE @@negate
|
|
|
|
@@successExit:
|
|
|
|
ADD ESP,12 // pop temp and saved copy of string pointer
|
|
|
|
XOR ESI,ESI // signal no error to caller
|
|
|
|
@@exit:
|
|
MOV [EDX],ESI
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
RET
|
|
|
|
@@negate:
|
|
FCHS
|
|
JMP @@successExit
|
|
|
|
@@empty:
|
|
INC ESI
|
|
|
|
@@error:
|
|
POP EAX
|
|
POP EBX
|
|
SUB ESI,EBX
|
|
ADD ESP,4
|
|
JMP @@exit
|
|
end;
|
|
|
|
procedure FPower10;
|
|
asm
|
|
JMP _Pow10
|
|
end;
|
|
|
|
//function _Pow10(val: Extended; Power: Integer): Extended;
|
|
procedure _Pow10;
|
|
asm
|
|
// -> FST(0) val
|
|
// -> EAX Power
|
|
// <- FST(0) val * 10**Power
|
|
|
|
// This routine generates 10**power with no more than two
|
|
// floating point multiplications. Up to 10**31, no multiplications
|
|
// are needed.
|
|
|
|
PUSH EBX
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
CALL GetGOT
|
|
MOV EBX,EAX
|
|
POP EAX
|
|
{$ELSE}
|
|
XOR EBX,EBX
|
|
{$ENDIF}
|
|
TEST EAX,EAX
|
|
JL @@neg
|
|
JE @@exit
|
|
CMP EAX,5120
|
|
JGE @@inf
|
|
MOV EDX,EAX
|
|
AND EDX,01FH
|
|
LEA EDX,[EDX+EDX*4]
|
|
FLD tbyte ptr @@tab0[EBX+EDX*2]
|
|
|
|
FMULP
|
|
|
|
SHR EAX,5
|
|
JE @@exit
|
|
|
|
MOV EDX,EAX
|
|
AND EDX,0FH
|
|
JE @@skip2ndMul
|
|
LEA EDX,[EDX+EDX*4]
|
|
FLD tbyte ptr @@tab1-10[EBX+EDX*2]
|
|
FMULP
|
|
|
|
@@skip2ndMul:
|
|
|
|
SHR EAX,4
|
|
JE @@exit
|
|
LEA EAX,[EAX+EAX*4]
|
|
FLD tbyte ptr @@tab2-10[EBX+EAX*2]
|
|
FMULP
|
|
JMP @@exit
|
|
|
|
@@neg:
|
|
NEG EAX
|
|
CMP EAX,5120
|
|
JGE @@zero
|
|
MOV EDX,EAX
|
|
AND EDX,01FH
|
|
LEA EDX,[EDX+EDX*4]
|
|
FLD tbyte ptr @@tab0[EBX+EDX*2]
|
|
FDIVP
|
|
|
|
SHR EAX,5
|
|
JE @@exit
|
|
|
|
MOV EDX,EAX
|
|
AND EDX,0FH
|
|
JE @@skip2ndDiv
|
|
LEA EDX,[EDX+EDX*4]
|
|
FLD tbyte ptr @@tab1-10[EBX+EDX*2]
|
|
FDIVP
|
|
|
|
@@skip2ndDiv:
|
|
|
|
SHR EAX,4
|
|
JE @@exit
|
|
LEA EAX,[EAX+EAX*4]
|
|
FLD tbyte ptr @@tab2-10[EBX+EAX*2]
|
|
FDIVP
|
|
|
|
JMP @@exit
|
|
|
|
@@inf:
|
|
FLD tbyte ptr @@infval[EBX]
|
|
JMP @@exit
|
|
|
|
@@zero:
|
|
FLDZ
|
|
|
|
@@exit:
|
|
POP EBX
|
|
RET
|
|
|
|
@@infval: DW $0000,$0000,$0000,$8000,$7FFF
|
|
@@tab0: DW $0000,$0000,$0000,$8000,$3FFF // 10**0
|
|
DW $0000,$0000,$0000,$A000,$4002 // 10**1
|
|
DW $0000,$0000,$0000,$C800,$4005 // 10**2
|
|
DW $0000,$0000,$0000,$FA00,$4008 // 10**3
|
|
DW $0000,$0000,$0000,$9C40,$400C // 10**4
|
|
DW $0000,$0000,$0000,$C350,$400F // 10**5
|
|
DW $0000,$0000,$0000,$F424,$4012 // 10**6
|
|
DW $0000,$0000,$8000,$9896,$4016 // 10**7
|
|
DW $0000,$0000,$2000,$BEBC,$4019 // 10**8
|
|
DW $0000,$0000,$2800,$EE6B,$401C // 10**9
|
|
DW $0000,$0000,$F900,$9502,$4020 // 10**10
|
|
DW $0000,$0000,$B740,$BA43,$4023 // 10**11
|
|
DW $0000,$0000,$A510,$E8D4,$4026 // 10**12
|
|
DW $0000,$0000,$E72A,$9184,$402A // 10**13
|
|
DW $0000,$8000,$20F4,$B5E6,$402D // 10**14
|
|
DW $0000,$A000,$A931,$E35F,$4030 // 10**15
|
|
DW $0000,$0400,$C9BF,$8E1B,$4034 // 10**16
|
|
DW $0000,$C500,$BC2E,$B1A2,$4037 // 10**17
|
|
DW $0000,$7640,$6B3A,$DE0B,$403A // 10**18
|
|
DW $0000,$89E8,$2304,$8AC7,$403E // 10**19
|
|
DW $0000,$AC62,$EBC5,$AD78,$4041 // 10**20
|
|
DW $8000,$177A,$26B7,$D8D7,$4044 // 10**21
|
|
DW $9000,$6EAC,$7832,$8786,$4048 // 10**22
|
|
DW $B400,$0A57,$163F,$A968,$404B // 10**23
|
|
DW $A100,$CCED,$1BCE,$D3C2,$404E // 10**24
|
|
DW $84A0,$4014,$5161,$8459,$4052 // 10**25
|
|
DW $A5C8,$9019,$A5B9,$A56F,$4055 // 10**26
|
|
DW $0F3A,$F420,$8F27,$CECB,$4058 // 10**27
|
|
DW $0984,$F894,$3978,$813F,$405C // 10**28
|
|
DW $0BE5,$36B9,$07D7,$A18F,$405F // 10**29
|
|
DW $4EDF,$0467,$C9CD,$C9F2,$4062 // 10**30
|
|
DW $2296,$4581,$7C40,$FC6F,$4065 // 10**31
|
|
|
|
@@tab1: DW $B59E,$2B70,$ADA8,$9DC5,$4069 // 10**32
|
|
DW $A6D5,$FFCF,$1F49,$C278,$40D3 // 10**64
|
|
DW $14A3,$C59B,$AB16,$EFB3,$413D // 10**96
|
|
DW $8CE0,$80E9,$47C9,$93BA,$41A8 // 10**128
|
|
DW $17AA,$7FE6,$A12B,$B616,$4212 // 10**160
|
|
DW $556B,$3927,$F78D,$E070,$427C // 10**192
|
|
DW $C930,$E33C,$96FF,$8A52,$42E7 // 10**224
|
|
DW $DE8E,$9DF9,$EBFB,$AA7E,$4351 // 10**256
|
|
DW $2F8C,$5C6A,$FC19,$D226,$43BB // 10**288
|
|
DW $E376,$F2CC,$2F29,$8184,$4426 // 10**320
|
|
DW $0AD2,$DB90,$2700,$9FA4,$4490 // 10**352
|
|
DW $AA17,$AEF8,$E310,$C4C5,$44FA // 10**384
|
|
DW $9C59,$E9B0,$9C07,$F28A,$4564 // 10**416
|
|
DW $F3D4,$EBF7,$4AE1,$957A,$45CF // 10**448
|
|
DW $A262,$0795,$D8DC,$B83E,$4639 // 10**480
|
|
|
|
@@tab2: DW $91C7,$A60E,$A0AE,$E319,$46A3 // 10**512
|
|
DW $0C17,$8175,$7586,$C976,$4D48 // 10**1024
|
|
DW $A7E4,$3993,$353B,$B2B8,$53ED // 10**1536
|
|
DW $5DE5,$C53D,$3B5D,$9E8B,$5A92 // 10**2048
|
|
DW $F0A6,$20A1,$54C0,$8CA5,$6137 // 10**2560
|
|
DW $5A8B,$D88B,$5D25,$F989,$67DB // 10**3072
|
|
DW $F3F8,$BF27,$C8A2,$DD5D,$6E80 // 10**3584
|
|
DW $979B,$8A20,$5202,$C460,$7525 // 10**4096
|
|
DW $59F0,$6ED5,$1162,$AE35,$7BCA // 10**4608
|
|
end;
|
|
|
|
const
|
|
RealBias = 129;
|
|
ExtBias = $3FFF;
|
|
|
|
procedure _Real2Ext;//( val : Real ) : Extended;
|
|
asm
|
|
// -> EAX Pointer to value
|
|
// <- FST(0) Result
|
|
|
|
// the REAL data type has the following format:
|
|
// 8 bit exponent (bias 129), 39 bit fraction, 1 bit sign
|
|
|
|
MOV DH,[EAX+5] // isolate the sign bit
|
|
AND DH,80H
|
|
MOV DL,[EAX] // fetch exponent
|
|
TEST DL,DL // exponent zero means number is zero
|
|
JE @@zero
|
|
|
|
ADD DX,ExtBias-RealBias // adjust exponent bias
|
|
|
|
PUSH EDX // the exponent is at the highest address
|
|
|
|
MOV EDX,[EAX+2] // load high fraction part, set hidden bit
|
|
OR EDX,80000000H
|
|
PUSH EDX // push high fraction part
|
|
|
|
MOV DL,[EAX+1] // load remaining low byte of fraction
|
|
SHL EDX,24 // clear low 24 bits
|
|
PUSH EDX
|
|
|
|
FLD tbyte ptr [ESP] // pop result onto chip
|
|
ADD ESP,12
|
|
|
|
RET
|
|
|
|
@@zero:
|
|
FLDZ
|
|
RET
|
|
end;
|
|
|
|
procedure _Ext2Real;//( val : Extended ) : Real;
|
|
asm
|
|
// -> FST(0) Value
|
|
// EAX Pointer to result
|
|
|
|
PUSH EBX
|
|
|
|
SUB ESP,12
|
|
FSTP tbyte ptr [ESP]
|
|
|
|
POP EBX // EBX is low half of fraction
|
|
POP EDX // EDX is high half of fraction
|
|
POP ECX // CX is exponent and sign
|
|
|
|
SHR EBX,24 // set carry to last bit shifted out
|
|
ADC BL,0 // if bit was 1, round up
|
|
ADC EDX,0
|
|
ADC CX,0
|
|
JO @@overflow
|
|
|
|
ADD EDX,EDX // shift fraction 1 bit left
|
|
ADD CX,CX // shift sign bit into carry
|
|
RCR EDX,1 // attach sign bit to fraction
|
|
SHR CX,1 // restore exponent, deleting sign
|
|
|
|
SUB CX,ExtBias-RealBias // adjust exponent
|
|
JLE @@underflow
|
|
TEST CH,CH // CX must be in 1..255
|
|
JG @@overflow
|
|
|
|
MOV [EAX],CL
|
|
MOV [EAX+1],BL
|
|
MOV [EAX+2],EDX
|
|
|
|
POP EBX
|
|
RET
|
|
|
|
@@underflow:
|
|
XOR ECX,ECX
|
|
MOV [EAX],ECX
|
|
MOV [EAX+4],CX
|
|
POP EBX
|
|
RET
|
|
|
|
@@overflow:
|
|
POP EBX
|
|
MOV AL,8
|
|
JMP Error
|
|
end;
|
|
|
|
const
|
|
ovtInstanceSize = -8; { Offset of instance size in OBJECTs }
|
|
ovtVmtPtrOffs = -4;
|
|
|
|
procedure _ObjSetup;
|
|
asm
|
|
{ FUNCTION _ObjSetup( self: ^OBJECT; vmt: ^VMT): ^OBJECT; }
|
|
{ ->EAX Pointer to self (possibly nil) }
|
|
{ EDX Pointer to vmt (possibly nil) }
|
|
{ <-EAX Pointer to self }
|
|
{ EDX <> 0: an object was allocated }
|
|
{ Z-Flag Set: failure, Cleared: Success }
|
|
|
|
CMP EDX,1 { is vmt = 0, indicating a call }
|
|
JAE @@skip1 { from a constructor? }
|
|
RET { return immediately with Z-flag cleared }
|
|
|
|
@@skip1:
|
|
PUSH ECX
|
|
TEST EAX,EAX { is self already allocated? }
|
|
JNE @@noAlloc
|
|
MOV EAX,[EDX].ovtInstanceSize
|
|
TEST EAX,EAX
|
|
JE @@zeroSize
|
|
PUSH EDX
|
|
CALL _GetMem
|
|
POP EDX
|
|
TEST EAX,EAX
|
|
JZ @@fail
|
|
|
|
{ Zero fill the memory }
|
|
PUSH EDI
|
|
MOV ECX,[EDX].ovtInstanceSize
|
|
MOV EDI,EAX
|
|
PUSH EAX
|
|
XOR EAX,EAX
|
|
SHR ECX,2
|
|
REP STOSD
|
|
MOV ECX,[EDX].ovtInstanceSize
|
|
AND ECX,3
|
|
REP STOSB
|
|
POP EAX
|
|
POP EDI
|
|
|
|
MOV ECX,[EDX].ovtVmtPtrOffs
|
|
TEST ECX,ECX
|
|
JL @@skip
|
|
MOV [EAX+ECX],EDX { store vmt in object at this offset }
|
|
@@skip:
|
|
TEST EAX,EAX { clear zero flag }
|
|
POP ECX
|
|
RET
|
|
|
|
@@fail:
|
|
XOR EDX,EDX
|
|
POP ECX
|
|
RET
|
|
|
|
@@zeroSize:
|
|
XOR EDX,EDX
|
|
CMP EAX,1 { clear zero flag - we were successful (kind of) }
|
|
POP ECX
|
|
RET
|
|
|
|
@@noAlloc:
|
|
MOV ECX,[EDX].ovtVmtPtrOffs
|
|
TEST ECX,ECX
|
|
JL @@exit
|
|
MOV [EAX+ECX],EDX { store vmt in object at this offset }
|
|
@@exit:
|
|
XOR EDX,EDX { clear allocated flag }
|
|
TEST EAX,EAX { clear zero flag }
|
|
POP ECX
|
|
end;
|
|
|
|
procedure _ObjCopy;
|
|
asm
|
|
{ PROCEDURE _ObjCopy( dest, src: ^OBJECT; vmtPtrOff: Longint); }
|
|
{ ->EAX Pointer to destination }
|
|
{ EDX Pointer to source }
|
|
{ ECX Offset of vmt in those objects. }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EDX
|
|
MOV EDI,EAX
|
|
|
|
LEA EAX,[EDI+ECX] { remember pointer to dest vmt pointer }
|
|
MOV EDX,[EAX] { fetch dest vmt pointer }
|
|
|
|
MOV EBX,[EDX].ovtInstanceSize
|
|
|
|
MOV ECX,EBX { copy size DIV 4 dwords }
|
|
SHR ECX,2
|
|
REP MOVSD
|
|
|
|
MOV ECX,EBX { copy size MOD 4 bytes }
|
|
AND ECX,3
|
|
REP MOVSB
|
|
|
|
MOV [EAX],EDX { restore dest vmt }
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _Fail;
|
|
asm
|
|
{ FUNCTION _Fail( self: ^OBJECT; allocFlag:Longint): ^OBJECT; }
|
|
{ ->EAX Pointer to self (possibly nil) }
|
|
{ EDX <> 0: Object must be deallocated }
|
|
{ <-EAX Nil }
|
|
|
|
TEST EDX,EDX
|
|
JE @@exit { if no object was allocated, return }
|
|
CALL _FreeMem
|
|
@@exit:
|
|
XOR EAX,EAX
|
|
end;
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
function GetKeyboardType(nTypeFlag: Integer): Integer; stdcall;
|
|
external user name 'GetKeyboardType';
|
|
|
|
function _isNECWindows: Boolean;
|
|
var
|
|
KbSubType: Integer;
|
|
begin
|
|
Result := False;
|
|
if GetKeyboardType(0) = $7 then
|
|
begin
|
|
KbSubType := GetKeyboardType(1) and $FF00;
|
|
if (KbSubType = $0D00) or (KbSubType = $0400) then
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
const
|
|
HKEY_LOCAL_MACHINE = $80000002;
|
|
|
|
// workaround a Japanese Win95 bug
|
|
procedure _FpuMaskInit;
|
|
const
|
|
KEY_QUERY_VALUE = $00000001;
|
|
REG_DWORD = 4;
|
|
FPUMASKKEY = 'SOFTWARE\Borland\Delphi\RTL';
|
|
FPUMASKNAME = 'FPUMaskValue';
|
|
var
|
|
phkResult: LongWord;
|
|
lpData, DataSize: Longint;
|
|
begin
|
|
lpData := Default8087CW;
|
|
|
|
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, FPUMASKKEY, 0, KEY_QUERY_VALUE, phkResult) = 0 then
|
|
try
|
|
DataSize := Sizeof(lpData);
|
|
RegQueryValueEx(phkResult, FPUMASKNAME, nil, nil, @lpData, @DataSize);
|
|
finally
|
|
RegCloseKey(phkResult);
|
|
end;
|
|
Default8087CW := (Default8087CW and $ffc0) or (lpData and $3f);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _FpuInit;
|
|
asm
|
|
FNINIT
|
|
FWAIT
|
|
{$IFDEF PIC}
|
|
CALL GetGOT
|
|
MOV EAX,[EAX].OFFSET Default8087CW
|
|
FLDCW [EAX]
|
|
{$ELSE}
|
|
FLDCW Default8087CW
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure FpuInit;
|
|
//const cwDefault: Word = $1332 { $133F};
|
|
asm
|
|
JMP _FpuInit
|
|
end;
|
|
|
|
procedure FpuInitConsiderNECWindows;
|
|
begin
|
|
if _isNECWindows then _FpuMaskInit;
|
|
FpuInit();
|
|
end;
|
|
|
|
procedure _BoundErr;
|
|
asm
|
|
MOV AL,reRangeError
|
|
JMP Error
|
|
end;
|
|
|
|
procedure _IntOver;
|
|
asm
|
|
MOV AL,reIntOverflow
|
|
JMP Error
|
|
end;
|
|
|
|
function TObject.ClassType: TClass;
|
|
begin
|
|
Pointer(Result) := PPointer(Self)^;
|
|
end;
|
|
|
|
class function TObject.ClassName: ShortString;
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
Result := PShortString(PPointer(Integer(Self) + vmtClassName)^)^;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX VMT }
|
|
{ EDX Pointer to result string }
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EDI,EDX
|
|
MOV ESI,[EAX].vmtClassName
|
|
XOR ECX,ECX
|
|
MOV CL,[ESI]
|
|
INC ECX
|
|
REP MOVSB
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
{$ENDIF}
|
|
|
|
class function TObject.ClassNameIs(const Name: string): Boolean;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
Temp: ShortString;
|
|
I: Byte;
|
|
begin
|
|
Result := False;
|
|
Temp := ClassName;
|
|
for I := 0 to Byte(Temp[0]) do
|
|
if Temp[I] <> Name[I] then Exit;
|
|
Result := True;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
PUSH EBX
|
|
XOR EBX,EBX
|
|
OR EDX,EDX
|
|
JE @@exit
|
|
MOV EAX,[EAX].vmtClassName
|
|
XOR ECX,ECX
|
|
MOV CL,[EAX]
|
|
CMP ECX,[EDX-4]
|
|
JNE @@exit
|
|
DEC EDX
|
|
@@loop:
|
|
MOV BH,[EAX+ECX]
|
|
XOR BH,[EDX+ECX]
|
|
AND BH,0DFH
|
|
JNE @@exit
|
|
DEC ECX
|
|
JNE @@loop
|
|
INC EBX
|
|
@@exit:
|
|
MOV AL,BL
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
class function TObject.ClassParent: TClass;
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
Pointer(Result) := PPointer(Integer(Self) + vmtParent)^;
|
|
if Result <> nil then
|
|
Pointer(Result) := PPointer(Result)^;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
MOV EAX,[EAX].vmtParent
|
|
TEST EAX,EAX
|
|
JE @@exit
|
|
MOV EAX,[EAX]
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
class function TObject.NewInstance: TObject;
|
|
begin
|
|
Result := InitInstance(_GetMem(InstanceSize));
|
|
end;
|
|
|
|
procedure TObject.FreeInstance;
|
|
begin
|
|
CleanupInstance;
|
|
_FreeMem(Self);
|
|
end;
|
|
|
|
class function TObject.InstanceSize: Longint;
|
|
begin
|
|
Result := PInteger(Integer(Self) + vmtInstanceSize)^;
|
|
end;
|
|
|
|
constructor TObject.Create;
|
|
begin
|
|
end;
|
|
|
|
destructor TObject.Destroy;
|
|
begin
|
|
end;
|
|
|
|
procedure TObject.Free;
|
|
begin
|
|
if Self <> nil then
|
|
Destroy;
|
|
end;
|
|
|
|
class function TObject.InitInstance(Instance: Pointer): TObject;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
IntfTable: PInterfaceTable;
|
|
ClassPtr: TClass;
|
|
I: Integer;
|
|
begin
|
|
FillChar(Instance^, InstanceSize, 0);
|
|
PInteger(Instance)^ := Integer(Self);
|
|
ClassPtr := Self;
|
|
while ClassPtr <> nil do
|
|
begin
|
|
IntfTable := ClassPtr.GetInterfaceTable;
|
|
if IntfTable <> nil then
|
|
for I := 0 to IntfTable.EntryCount-1 do
|
|
with IntfTable.Entries[I] do
|
|
begin
|
|
if VTable <> nil then
|
|
PInteger(@PChar(Instance)[IOffset])^ := Integer(VTable);
|
|
end;
|
|
ClassPtr := ClassPtr.ClassParent;
|
|
end;
|
|
Result := Instance;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EBX,EAX
|
|
MOV EDI,EDX
|
|
STOSD
|
|
MOV ECX,[EBX].vmtInstanceSize
|
|
XOR EAX,EAX
|
|
PUSH ECX
|
|
SHR ECX,2
|
|
DEC ECX
|
|
REP STOSD
|
|
POP ECX
|
|
AND ECX,3
|
|
REP STOSB
|
|
MOV EAX,EDX
|
|
MOV EDX,ESP
|
|
@@0: MOV ECX,[EBX].vmtIntfTable
|
|
TEST ECX,ECX
|
|
JE @@1
|
|
PUSH ECX
|
|
@@1: MOV EBX,[EBX].vmtParent
|
|
TEST EBX,EBX
|
|
JE @@2
|
|
MOV EBX,[EBX]
|
|
JMP @@0
|
|
@@2: CMP ESP,EDX
|
|
JE @@5
|
|
@@3: POP EBX
|
|
MOV ECX,[EBX].TInterfaceTable.EntryCount
|
|
ADD EBX,4
|
|
@@4: MOV ESI,[EBX].TInterfaceEntry.VTable
|
|
TEST ESI,ESI
|
|
JE @@4a
|
|
MOV EDI,[EBX].TInterfaceEntry.IOffset
|
|
MOV [EAX+EDI],ESI
|
|
@@4a: ADD EBX,TYPE TInterfaceEntry
|
|
DEC ECX
|
|
JNE @@4
|
|
CMP ESP,EDX
|
|
JNE @@3
|
|
@@5: POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TObject.CleanupInstance;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
ClassPtr: TClass;
|
|
InitTable: Pointer;
|
|
begin
|
|
ClassPtr := ClassType;
|
|
InitTable := PPointer(Integer(ClassPtr) + vmtInitTable)^;
|
|
while (ClassPtr <> nil) and (InitTable <> nil) do
|
|
begin
|
|
_FinalizeRecord(Self, InitTable);
|
|
ClassPtr := ClassPtr.ClassParent;
|
|
if ClassPtr <> nil then
|
|
InitTable := PPointer(Integer(ClassPtr) + vmtInitTable)^;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV EBX,EAX
|
|
MOV ESI,EAX
|
|
@@loop:
|
|
MOV ESI,[ESI]
|
|
MOV EDX,[ESI].vmtInitTable
|
|
MOV ESI,[ESI].vmtParent
|
|
TEST EDX,EDX
|
|
JE @@skip
|
|
CALL _FinalizeRecord
|
|
MOV EAX,EBX
|
|
@@skip:
|
|
TEST ESI,ESI
|
|
JNE @@loop
|
|
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function InvokeImplGetter(Self: TObject; ImplGetter: Cardinal): IInterface;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
M: function: IInterface of object;
|
|
begin
|
|
TMethod(M).Data := Self;
|
|
case ImplGetter of
|
|
$FF000000..$FFFFFFFF: // Field
|
|
Result := IInterface(Pointer(Cardinal(Self) + (ImplGetter and $00FFFFFF)));
|
|
$FE000000..$FEFFFFFF: // virtual method
|
|
begin
|
|
// sign extend vmt slot offset = smallint cast
|
|
TMethod(M).Code := PPointer(Integer(Self) + SmallInt(ImplGetter))^;
|
|
Result := M;
|
|
end;
|
|
else // static method
|
|
TMethod(M).Code := Pointer(ImplGetter);
|
|
Result := M;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
XCHG EDX,ECX
|
|
CMP ECX,$FF000000
|
|
JAE @@isField
|
|
CMP ECX,$FE000000
|
|
JB @@isStaticMethod
|
|
|
|
{ the GetProc is a virtual method }
|
|
MOVSX ECX,CX { sign extend slot offs }
|
|
ADD ECX,[EAX] { vmt + slotoffs }
|
|
JMP dword ptr [ECX] { call vmt[slot] }
|
|
|
|
@@isStaticMethod:
|
|
JMP ECX
|
|
|
|
@@isField:
|
|
AND ECX,$00FFFFFF
|
|
ADD ECX,EAX
|
|
MOV EAX,EDX
|
|
MOV EDX,[ECX]
|
|
JMP _IntfCopy
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function TObject.GetInterface(const IID: TGUID; out Obj): Boolean;
|
|
var
|
|
InterfaceEntry: PInterfaceEntry;
|
|
begin
|
|
Pointer(Obj) := nil;
|
|
InterfaceEntry := GetInterfaceEntry(IID);
|
|
if InterfaceEntry <> nil then
|
|
begin
|
|
if InterfaceEntry^.IOffset <> 0 then
|
|
begin
|
|
Pointer(Obj) := Pointer(Integer(Self) + InterfaceEntry^.IOffset);
|
|
if Pointer(Obj) <> nil then IInterface(Obj)._AddRef;
|
|
end
|
|
else
|
|
IInterface(Obj) := InvokeImplGetter(Self, InterfaceEntry^.ImplGetter);
|
|
end;
|
|
Result := Pointer(Obj) <> nil;
|
|
end;
|
|
|
|
class function TObject.GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
ClassPtr: TClass;
|
|
IntfTable: PInterfaceTable;
|
|
I: Integer;
|
|
begin
|
|
ClassPtr := Self;
|
|
while ClassPtr <> nil do
|
|
begin
|
|
IntfTable := ClassPtr.GetInterfaceTable;
|
|
if IntfTable <> nil then
|
|
for I := 0 to IntfTable.EntryCount-1 do
|
|
begin
|
|
Result := @IntfTable.Entries[I];
|
|
// if Result^.IID = IID then Exit;
|
|
if (Int64(Result^.IID.D1) = Int64(IID.D1)) and
|
|
(Int64(Result^.IID.D4) = Int64(IID.D4)) then Exit;
|
|
end;
|
|
ClassPtr := ClassPtr.ClassParent;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV EBX,EAX
|
|
@@1: MOV EAX,[EBX].vmtIntfTable
|
|
TEST EAX,EAX
|
|
JE @@4
|
|
MOV ECX,[EAX].TInterfaceTable.EntryCount
|
|
ADD EAX,4
|
|
@@2: MOV ESI,[EDX].Integer[0]
|
|
CMP ESI,[EAX].TInterfaceEntry.IID.Integer[0]
|
|
JNE @@3
|
|
MOV ESI,[EDX].Integer[4]
|
|
CMP ESI,[EAX].TInterfaceEntry.IID.Integer[4]
|
|
JNE @@3
|
|
MOV ESI,[EDX].Integer[8]
|
|
CMP ESI,[EAX].TInterfaceEntry.IID.Integer[8]
|
|
JNE @@3
|
|
MOV ESI,[EDX].Integer[12]
|
|
CMP ESI,[EAX].TInterfaceEntry.IID.Integer[12]
|
|
JE @@5
|
|
@@3: ADD EAX,type TInterfaceEntry
|
|
DEC ECX
|
|
JNE @@2
|
|
@@4: MOV EBX,[EBX].vmtParent
|
|
TEST EBX,EBX
|
|
JE @@4a
|
|
MOV EBX,[EBX]
|
|
JMP @@1
|
|
@@4a: XOR EAX,EAX
|
|
@@5: POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
class function TObject.GetInterfaceTable: PInterfaceTable;
|
|
begin
|
|
Result := PPointer(Integer(Self) + vmtIntfTable)^;
|
|
end;
|
|
|
|
function _IsClass(Child: TObject; Parent: TClass): Boolean;
|
|
begin
|
|
Result := (Child <> nil) and Child.InheritsFrom(Parent);
|
|
end;
|
|
|
|
function _AsClass(Child: TObject; Parent: TClass): TObject;
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
Result := Child;
|
|
if not (Child is Parent) then
|
|
Error(reInvalidCast); // loses return address
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX left operand (class) }
|
|
{ EDX VMT of right operand }
|
|
{ <- EAX if left is derived from right, else runtime error }
|
|
TEST EAX,EAX
|
|
JE @@exit
|
|
MOV ECX,EAX
|
|
@@loop:
|
|
MOV ECX,[ECX]
|
|
CMP ECX,EDX
|
|
JE @@exit
|
|
MOV ECX,[ECX].vmtParent
|
|
TEST ECX,ECX
|
|
JNE @@loop
|
|
|
|
{ do runtime error }
|
|
MOV AL,reInvalidCast
|
|
JMP Error
|
|
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
procedure GetDynaMethod;
|
|
{ function GetDynaMethod(vmt: TClass; selector: Smallint) : Pointer; }
|
|
asm
|
|
{ -> EAX vmt of class }
|
|
{ SI dynamic method index }
|
|
{ <- ESI pointer to routine }
|
|
{ ZF = 0 if found }
|
|
{ trashes: EAX, ECX }
|
|
|
|
PUSH EDI
|
|
XCHG EAX,ESI
|
|
JMP @@haveVMT
|
|
@@outerLoop:
|
|
MOV ESI,[ESI]
|
|
@@haveVMT:
|
|
MOV EDI,[ESI].vmtDynamicTable
|
|
TEST EDI,EDI
|
|
JE @@parent
|
|
MOVZX ECX,word ptr [EDI]
|
|
PUSH ECX
|
|
ADD EDI,2
|
|
REPNE SCASW
|
|
JE @@found
|
|
POP ECX
|
|
@@parent:
|
|
MOV ESI,[ESI].vmtParent
|
|
TEST ESI,ESI
|
|
JNE @@outerLoop
|
|
JMP @@exit
|
|
|
|
@@found:
|
|
POP EAX
|
|
ADD EAX,EAX
|
|
SUB EAX,ECX { this will always clear the Z-flag ! }
|
|
MOV ESI,[EDI+EAX*2-4]
|
|
|
|
@@exit:
|
|
POP EDI
|
|
end;
|
|
|
|
procedure _CallDynaInst;
|
|
asm
|
|
PUSH EAX
|
|
PUSH ECX
|
|
MOV EAX,[EAX]
|
|
CALL GetDynaMethod
|
|
POP ECX
|
|
POP EAX
|
|
JE @@Abstract
|
|
JMP ESI
|
|
@@Abstract:
|
|
POP ECX
|
|
JMP _AbstractError
|
|
end;
|
|
|
|
|
|
procedure _CallDynaClass;
|
|
asm
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL GetDynaMethod
|
|
POP ECX
|
|
POP EAX
|
|
JE @@Abstract
|
|
JMP ESI
|
|
@@Abstract:
|
|
POP ECX
|
|
JMP _AbstractError
|
|
end;
|
|
|
|
|
|
procedure _FindDynaInst;
|
|
asm
|
|
PUSH ESI
|
|
MOV ESI,EDX
|
|
MOV EAX,[EAX]
|
|
CALL GetDynaMethod
|
|
MOV EAX,ESI
|
|
POP ESI
|
|
JNE @@exit
|
|
POP ECX
|
|
JMP _AbstractError
|
|
@@exit:
|
|
end;
|
|
|
|
|
|
procedure _FindDynaClass;
|
|
asm
|
|
PUSH ESI
|
|
MOV ESI,EDX
|
|
CALL GetDynaMethod
|
|
MOV EAX,ESI
|
|
POP ESI
|
|
JNE @@exit
|
|
POP ECX
|
|
JMP _AbstractError
|
|
@@exit:
|
|
end;
|
|
|
|
class function TObject.InheritsFrom(AClass: TClass): Boolean;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
ClassPtr: TClass;
|
|
begin
|
|
ClassPtr := Self;
|
|
while (ClassPtr <> nil) and (ClassPtr <> AClass) do
|
|
ClassPtr := PPointer(Integer(ClassPtr) + vmtParent)^;
|
|
Result := ClassPtr = AClass;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX Pointer to our class }
|
|
{ EDX Pointer to AClass }
|
|
{ <- AL Boolean result }
|
|
JMP @@haveVMT
|
|
@@loop:
|
|
MOV EAX,[EAX]
|
|
@@haveVMT:
|
|
CMP EAX,EDX
|
|
JE @@success
|
|
MOV EAX,[EAX].vmtParent
|
|
TEST EAX,EAX
|
|
JNE @@loop
|
|
JMP @@exit
|
|
@@success:
|
|
MOV AL,1
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
class function TObject.ClassInfo: Pointer;
|
|
begin
|
|
Result := PPointer(Integer(Self) + vmtTypeInfo)^;
|
|
end;
|
|
|
|
|
|
function TObject.SafeCallException(ExceptObject: TObject;
|
|
ExceptAddr: Pointer): HResult;
|
|
begin
|
|
Result := HResult($8000FFFF); { E_UNEXPECTED }
|
|
end;
|
|
|
|
|
|
procedure TObject.DefaultHandler(var Message);
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure TObject.AfterConstruction;
|
|
begin
|
|
end;
|
|
|
|
procedure TObject.BeforeDestruction;
|
|
begin
|
|
end;
|
|
|
|
procedure TObject.Dispatch(var Message);
|
|
asm
|
|
PUSH ESI
|
|
MOV SI,[EDX]
|
|
OR SI,SI
|
|
JE @@default
|
|
CMP SI,0C000H
|
|
JAE @@default
|
|
PUSH EAX
|
|
MOV EAX,[EAX]
|
|
CALL GetDynaMethod
|
|
POP EAX
|
|
JE @@default
|
|
MOV ECX,ESI
|
|
POP ESI
|
|
JMP ECX
|
|
|
|
@@default:
|
|
POP ESI
|
|
MOV ECX,[EAX]
|
|
JMP dword ptr [ECX].vmtDefaultHandler
|
|
end;
|
|
|
|
|
|
class function TObject.MethodAddress(const Name: ShortString): Pointer;
|
|
asm
|
|
{ -> EAX Pointer to class }
|
|
{ EDX Pointer to name }
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
XOR ECX,ECX
|
|
XOR EDI,EDI
|
|
MOV BL,[EDX]
|
|
JMP @@haveVMT
|
|
@@outer: { upper 16 bits of ECX are 0 ! }
|
|
MOV EAX,[EAX]
|
|
@@haveVMT:
|
|
MOV ESI,[EAX].vmtMethodTable
|
|
TEST ESI,ESI
|
|
JE @@parent
|
|
MOV DI,[ESI] { EDI := method count }
|
|
ADD ESI,2
|
|
@@inner: { upper 16 bits of ECX are 0 ! }
|
|
MOV CL,[ESI+6] { compare length of strings }
|
|
CMP CL,BL
|
|
JE @@cmpChar
|
|
@@cont: { upper 16 bits of ECX are 0 ! }
|
|
MOV CX,[ESI] { fetch length of method desc }
|
|
ADD ESI,ECX { point ESI to next method }
|
|
DEC EDI
|
|
JNZ @@inner
|
|
@@parent:
|
|
MOV EAX,[EAX].vmtParent { fetch parent vmt }
|
|
TEST EAX,EAX
|
|
JNE @@outer
|
|
JMP @@exit { return NIL }
|
|
|
|
@@notEqual:
|
|
MOV BL,[EDX] { restore BL to length of name }
|
|
JMP @@cont
|
|
|
|
@@cmpChar: { upper 16 bits of ECX are 0 ! }
|
|
MOV CH,0 { upper 24 bits of ECX are 0 ! }
|
|
@@cmpCharLoop:
|
|
MOV BL,[ESI+ECX+6] { case insensitive string cmp }
|
|
XOR BL,[EDX+ECX+0] { last char is compared first }
|
|
AND BL,$DF
|
|
JNE @@notEqual
|
|
DEC ECX { ECX serves as counter }
|
|
JNZ @@cmpCharLoop
|
|
|
|
{ found it }
|
|
MOV EAX,[ESI+2]
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
class function TObject.MethodName(Address: Pointer): ShortString;
|
|
asm
|
|
{ -> EAX Pointer to class }
|
|
{ EDX Address }
|
|
{ ECX Pointer to result }
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EDI,ECX
|
|
XOR EBX,EBX
|
|
XOR ECX,ECX
|
|
JMP @@haveVMT
|
|
@@outer:
|
|
MOV EAX,[EAX]
|
|
@@haveVMT:
|
|
MOV ESI,[EAX].vmtMethodTable { fetch pointer to method table }
|
|
TEST ESI,ESI
|
|
JE @@parent
|
|
MOV CX,[ESI]
|
|
ADD ESI,2
|
|
@@inner:
|
|
CMP EDX,[ESI+2]
|
|
JE @@found
|
|
MOV BX,[ESI]
|
|
ADD ESI,EBX
|
|
DEC ECX
|
|
JNZ @@inner
|
|
@@parent:
|
|
MOV EAX,[EAX].vmtParent
|
|
TEST EAX,EAX
|
|
JNE @@outer
|
|
MOV [EDI],AL
|
|
JMP @@exit
|
|
|
|
@@found:
|
|
ADD ESI,6
|
|
XOR ECX,ECX
|
|
MOV CL,[ESI]
|
|
INC ECX
|
|
REP MOVSB
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
function TObject.FieldAddress(const Name: ShortString): Pointer;
|
|
asm
|
|
{ -> EAX Pointer to instance }
|
|
{ EDX Pointer to name }
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
XOR ECX,ECX
|
|
XOR EDI,EDI
|
|
MOV BL,[EDX]
|
|
|
|
PUSH EAX { save instance pointer }
|
|
|
|
@@outer:
|
|
MOV EAX,[EAX] { fetch class pointer }
|
|
MOV ESI,[EAX].vmtFieldTable
|
|
TEST ESI,ESI
|
|
JE @@parent
|
|
MOV DI,[ESI] { fetch count of fields }
|
|
ADD ESI,6
|
|
@@inner:
|
|
MOV CL,[ESI+6] { compare string lengths }
|
|
CMP CL,BL
|
|
JE @@cmpChar
|
|
@@cont:
|
|
LEA ESI,[ESI+ECX+7] { point ESI to next field }
|
|
DEC EDI
|
|
JNZ @@inner
|
|
@@parent:
|
|
MOV EAX,[EAX].vmtParent { fetch parent VMT }
|
|
TEST EAX,EAX
|
|
JNE @@outer
|
|
POP EDX { forget instance, return Nil }
|
|
JMP @@exit
|
|
|
|
@@notEqual:
|
|
MOV BL,[EDX] { restore BL to length of name }
|
|
MOV CL,[ESI+6] { ECX := length of field name }
|
|
JMP @@cont
|
|
|
|
@@cmpChar:
|
|
MOV BL,[ESI+ECX+6] { case insensitive string cmp }
|
|
XOR BL,[EDX+ECX+0] { starting with last char }
|
|
AND BL,$DF
|
|
JNE @@notEqual
|
|
DEC ECX { ECX serves as counter }
|
|
JNZ @@cmpChar
|
|
|
|
{ found it }
|
|
MOV EAX,[ESI] { result is field offset plus ... }
|
|
POP EDX
|
|
ADD EAX,EDX { instance pointer }
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
|
|
asm
|
|
{ -> EAX = pointer to VMT }
|
|
{ <- EAX = pointer to instance }
|
|
PUSH EDX
|
|
PUSH ECX
|
|
PUSH EBX
|
|
TEST DL,DL
|
|
JL @@noAlloc
|
|
CALL dword ptr [EAX].vmtNewInstance
|
|
@@noAlloc:
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
XOR EDX,EDX
|
|
LEA ECX,[ESP+16]
|
|
MOV EBX,FS:[EDX]
|
|
MOV [ECX].TExcFrame.next,EBX
|
|
MOV [ECX].TExcFrame.hEBP,EBP
|
|
MOV [ECX].TExcFrame.desc,offset @desc
|
|
MOV [ECX].TexcFrame.ConstructedObject,EAX { trick: remember copy to instance }
|
|
MOV FS:[EDX],ECX
|
|
{$ENDIF}
|
|
POP EBX
|
|
POP ECX
|
|
POP EDX
|
|
RET
|
|
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
@desc:
|
|
JMP _HandleAnyException
|
|
|
|
{ destroy the object }
|
|
|
|
MOV EAX,[ESP+8+9*4]
|
|
MOV EAX,[EAX].TExcFrame.ConstructedObject
|
|
TEST EAX,EAX
|
|
JE @@skip
|
|
MOV ECX,[EAX]
|
|
MOV DL,$81
|
|
PUSH EAX
|
|
CALL dword ptr [ECX].vmtDestroy
|
|
POP EAX
|
|
CALL _ClassDestroy
|
|
@@skip:
|
|
{ reraise the exception }
|
|
CALL _RaiseAgain
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _ClassDestroy(Instance: TObject);
|
|
begin
|
|
Instance.FreeInstance;
|
|
end;
|
|
|
|
|
|
function _AfterConstruction(Instance: TObject): TObject;
|
|
begin
|
|
Instance.AfterConstruction;
|
|
Result := Instance;
|
|
end;
|
|
|
|
function _BeforeDestruction(Instance: TObject; OuterMost: ShortInt): TObject;
|
|
// Must preserve DL on return!
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
Result := Instance;
|
|
if OuterMost > 0 then Exit;
|
|
Instance.BeforeDestruction;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX = pointer to instance }
|
|
{ DL = dealloc flag }
|
|
|
|
TEST DL,DL
|
|
JG @@outerMost
|
|
RET
|
|
@@outerMost:
|
|
PUSH EAX
|
|
PUSH EDX
|
|
MOV EDX,[EAX]
|
|
CALL dword ptr [EDX].vmtBeforeDestruction
|
|
POP EDX
|
|
POP EAX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{
|
|
The following NotifyXXXX routines are used to "raise" special exceptions
|
|
as a signaling mechanism to an interested debugger. If the debugger sets
|
|
the DebugHook flag to 1 or 2, then all exception processing is tracked by
|
|
raising these special exceptions. The debugger *MUST* respond to the
|
|
debug event with DBG_CONTINE so that normal processing will occur.
|
|
}
|
|
|
|
{$IFDEF LINUX}
|
|
const
|
|
excRaise = 0; { an exception is being raised by the user (could be a reraise) }
|
|
excCatch = 1; { an exception is about to be caught }
|
|
excFinally = 2; { a finally block is about to be executed because of an exception }
|
|
excUnhandled = 3; { no user exception handler was found (the app will die) }
|
|
|
|
procedure _DbgExcNotify(
|
|
NotificationKind: Integer;
|
|
ExceptionObject: Pointer;
|
|
ExceptionName: PShortString;
|
|
ExceptionLocation: Pointer;
|
|
HandlerAddr: Pointer); cdecl; export;
|
|
begin
|
|
{$IFDEF DEBUG}
|
|
{
|
|
This code is just for debugging the exception handling system. The debugger
|
|
needs _DbgExcNotify, however to place breakpoints in, so the function itself
|
|
cannot be removed.
|
|
}
|
|
asm
|
|
PUSH EAX
|
|
PUSH EDX
|
|
end;
|
|
if Assigned(ExcNotificationProc) then
|
|
ExcNotificationProc(NotificationKind, ExceptionObject, ExceptionName, ExceptionLocation, HandlerAddr);
|
|
asm
|
|
POP EDX
|
|
POP EAX
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{
|
|
The following functions are used by the debugger for the evaluator. If you
|
|
change them IN ANY WAY, the debugger will cease to function correctly.
|
|
}
|
|
procedure _DbgEvalMarker;
|
|
begin
|
|
end;
|
|
|
|
procedure _DbgEvalExcept(E: TObject);
|
|
begin
|
|
end;
|
|
|
|
procedure _DbgEvalEnd;
|
|
begin
|
|
end;
|
|
|
|
{
|
|
This function is used by the debugger to provide a soft landing spot
|
|
when evaluating a function call that may raise an unhandled exception.
|
|
The return address of _DbgEvalMarker is pushed onto the stack so that
|
|
the unwinder will transfer control to the except block.
|
|
}
|
|
procedure _DbgEvalFrame;
|
|
begin
|
|
try
|
|
_DbgEvalMarker;
|
|
except on E: TObject do
|
|
_DbgEvalExcept(E);
|
|
end;
|
|
_DbgEvalEnd;
|
|
end;
|
|
|
|
{
|
|
These export names need to match the names that will be generated into
|
|
the .symtab section, so that the debugger can find them if stabs
|
|
debug information is being generated.
|
|
}
|
|
exports
|
|
_DbgExcNotify name '@DbgExcNotify',
|
|
_DbgEvalFrame name '@DbgEvalFrame',
|
|
_DbgEvalMarker name '@DbgEvalMarker',
|
|
_DbgEvalExcept name '@DbgEvalExcept',
|
|
_DbgEvalEnd name '@DbgEvalEnd';
|
|
{$ENDIF}
|
|
|
|
{ tell the debugger that the next raise is a re-raise of the current non-Delphi
|
|
exception }
|
|
procedure NotifyReRaise;
|
|
asm
|
|
{$IFDEF LINUX}
|
|
{ ->EAX Pointer to exception object }
|
|
{ EDX location of exception }
|
|
PUSH 0 { handler addr }
|
|
PUSH EDX { location of exception }
|
|
MOV ECX, [EAX]
|
|
PUSH [ECX].vmtClassName { exception name }
|
|
PUSH EAX { exception object }
|
|
PUSH excRaise { notification kind }
|
|
CALL _DbgExcNotify
|
|
ADD ESP, 20
|
|
{$ELSE}
|
|
CMP BYTE PTR DebugHook,1
|
|
JBE @@1
|
|
PUSH 0
|
|
PUSH 0
|
|
PUSH cContinuable
|
|
PUSH cDelphiReRaise
|
|
CALL RaiseExceptionProc
|
|
@@1:
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ tell the debugger about the raise of a non-Delphi exception }
|
|
{$IFNDEF LINUX}
|
|
procedure NotifyNonDelphiException;
|
|
asm
|
|
CMP BYTE PTR DebugHook,0
|
|
JE @@1
|
|
PUSH EAX
|
|
PUSH EAX
|
|
PUSH EDX
|
|
PUSH ESP
|
|
PUSH 2
|
|
PUSH cContinuable
|
|
PUSH cNonDelphiException
|
|
CALL RaiseExceptionProc
|
|
ADD ESP,8
|
|
POP EAX
|
|
@@1:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ Tell the debugger where the handler for the current exception is located }
|
|
procedure NotifyExcept;
|
|
asm
|
|
{$IFDEF LINUX}
|
|
{ ->EAX Pointer to exception object }
|
|
{ EDX handler addr }
|
|
PUSH EAX
|
|
MOV EAX, [EAX].TRaisedException.ExceptObject
|
|
|
|
PUSH EDX { handler addr }
|
|
PUSH 0 { location of exception }
|
|
MOV ECX, [EAX]
|
|
PUSH [ECX].vmtClassName { exception name }
|
|
PUSH EAX { exception object }
|
|
PUSH excCatch { notification kind }
|
|
CALL _DbgExcNotify
|
|
ADD ESP, 20
|
|
|
|
POP EAX
|
|
{$ELSE}
|
|
PUSH ESP
|
|
PUSH 1
|
|
PUSH cContinuable
|
|
PUSH cDelphiExcept { our magic exception code }
|
|
CALL RaiseExceptionProc
|
|
ADD ESP,4
|
|
POP EAX
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure NotifyOnExcept;
|
|
asm
|
|
{$IFDEF LINUX}
|
|
{ ->EAX Pointer to exception object }
|
|
{ EDX handler addr }
|
|
PUSH EDX { handler addr }
|
|
PUSH 0 { location of exception }
|
|
MOV ECX, [EAX]
|
|
PUSH [ECX].vmtClassName { exception name }
|
|
PUSH EAX { exception object }
|
|
PUSH excCatch { notification kind }
|
|
CALL _DbgExcNotify
|
|
ADD ESP, 20
|
|
{$ELSE}
|
|
CMP BYTE PTR DebugHook,1
|
|
JBE @@1
|
|
PUSH EAX
|
|
PUSH [EBX].TExcDescEntry.handler
|
|
JMP NotifyExcept
|
|
@@1:
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFNDEF LINUX}
|
|
procedure NotifyAnyExcept;
|
|
asm
|
|
CMP BYTE PTR DebugHook,1
|
|
JBE @@1
|
|
PUSH EAX
|
|
PUSH EBX
|
|
JMP NotifyExcept
|
|
@@1:
|
|
end;
|
|
|
|
procedure CheckJmp;
|
|
asm
|
|
TEST ECX,ECX
|
|
JE @@3
|
|
MOV EAX,[ECX + 1]
|
|
CMP BYTE PTR [ECX],0E9H { near jmp }
|
|
JE @@1
|
|
CMP BYTE PTR [ECX],0EBH { short jmp }
|
|
JNE @@3
|
|
MOVSX EAX,AL
|
|
INC ECX
|
|
INC ECX
|
|
JMP @@2
|
|
@@1:
|
|
ADD ECX,5
|
|
@@2:
|
|
ADD ECX,EAX
|
|
@@3:
|
|
end;
|
|
{$ENDIF} { not LINUX }
|
|
|
|
{ Notify debugger of a finally during an exception unwind }
|
|
procedure NotifyExceptFinally;
|
|
asm
|
|
{$IFDEF LINUX}
|
|
{ ->EAX Pointer to exception object }
|
|
{ EDX handler addr }
|
|
PUSH EDX { handler addr }
|
|
PUSH 0 { location of exception }
|
|
PUSH 0 { exception name }
|
|
PUSH 0 { exception object }
|
|
PUSH excFinally { notification kind }
|
|
CALL _DbgExcNotify
|
|
ADD ESP, 20
|
|
{$ELSE}
|
|
CMP BYTE PTR DebugHook,1
|
|
JBE @@1
|
|
PUSH EAX
|
|
PUSH EDX
|
|
PUSH ECX
|
|
CALL CheckJmp
|
|
PUSH ECX
|
|
PUSH ESP { pass pointer to arguments }
|
|
PUSH 1 { there is 1 argument }
|
|
PUSH cContinuable { continuable execution }
|
|
PUSH cDelphiFinally { our magic exception code }
|
|
CALL RaiseExceptionProc
|
|
POP ECX
|
|
POP ECX
|
|
POP EDX
|
|
POP EAX
|
|
@@1:
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
{ Tell the debugger that the current exception is handled and cleaned up.
|
|
Also indicate where execution is about to resume. }
|
|
{$IFNDEF LINUX}
|
|
procedure NotifyTerminate;
|
|
asm
|
|
CMP BYTE PTR DebugHook,1
|
|
JBE @@1
|
|
PUSH EDX
|
|
PUSH ESP
|
|
PUSH 1
|
|
PUSH cContinuable
|
|
PUSH cDelphiTerminate { our magic exception code }
|
|
CALL RaiseExceptionProc
|
|
POP EDX
|
|
@@1:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ Tell the debugger that there was no handler found for the current exception
|
|
and we are about to go to the default handler }
|
|
procedure NotifyUnhandled;
|
|
asm
|
|
{$IFDEF LINUX}
|
|
{ ->EAX Pointer to exception object }
|
|
{ EDX location of exception }
|
|
PUSH EAX
|
|
MOV EAX, [EAX].TRaisedException.ExceptObject
|
|
|
|
PUSH 0 { handler addr }
|
|
PUSH EDX { location of exception }
|
|
MOV ECX, [EAX]
|
|
PUSH [ECX].vmtClassName { exception name }
|
|
PUSH EAX { exception object }
|
|
PUSH excUnhandled { notification kind }
|
|
CALL _DbgExcNotify
|
|
ADD ESP, 20
|
|
|
|
POP EAX
|
|
{$ELSE}
|
|
PUSH EAX
|
|
PUSH EDX
|
|
CMP BYTE PTR DebugHook,1
|
|
JBE @@1
|
|
PUSH ESP
|
|
PUSH 2
|
|
PUSH cContinuable
|
|
PUSH cDelphiUnhandled
|
|
CALL RaiseExceptionProc
|
|
@@1:
|
|
POP EDX
|
|
POP EAX
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _HandleAnyException;
|
|
asm
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
CALL UnblockOSExceptions
|
|
OR [EAX].TRaisedException.Flags, excIsBeingHandled
|
|
MOV ESI, EBX
|
|
MOV EDX, [ESP]
|
|
CALL NotifyExcept
|
|
MOV EBX, ESI
|
|
{$ENDIF}
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
{ -> [ESP+ 4] excPtr: PExceptionRecord }
|
|
{ [ESP+ 8] errPtr: PExcFrame }
|
|
{ [ESP+12] ctxPtr: Pointer }
|
|
{ [ESP+16] dspPtr: Pointer }
|
|
{ <- EAX return value - always one }
|
|
|
|
MOV EAX,[ESP+4]
|
|
TEST [EAX].TExceptionRecord.ExceptionFlags,cUnwindInProgress
|
|
JNE @@exit
|
|
|
|
CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException
|
|
MOV EDX,[EAX].TExceptionRecord.ExceptObject
|
|
MOV ECX,[EAX].TExceptionRecord.ExceptAddr
|
|
JE @@DelphiException
|
|
CLD
|
|
CALL _FpuInit
|
|
MOV EDX,ExceptObjProc
|
|
TEST EDX,EDX
|
|
JE @@exit
|
|
CALL EDX
|
|
TEST EAX,EAX
|
|
JE @@exit
|
|
MOV EDX,[ESP+12]
|
|
MOV ECX,[ESP+4]
|
|
CMP [ECX].TExceptionRecord.ExceptionCode,cCppException
|
|
JE @@CppException
|
|
CALL NotifyNonDelphiException
|
|
{$IFDEF MSWINDOWS}
|
|
CMP BYTE PTR JITEnable,0
|
|
JBE @@CppException
|
|
CMP BYTE PTR DebugHook,0
|
|
JA @@CppException // Do not JIT if debugging
|
|
LEA ECX,[ESP+4]
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL UnhandledExceptionFilter
|
|
CMP EAX,EXCEPTION_CONTINUE_SEARCH
|
|
POP EAX
|
|
JE @@exit
|
|
MOV EDX,EAX
|
|
MOV EAX,[ESP+4]
|
|
MOV ECX,[EAX].TExceptionRecord.ExceptionAddress
|
|
JMP @@GoUnwind
|
|
{$ENDIF}
|
|
@@CppException:
|
|
MOV EDX,EAX
|
|
MOV EAX,[ESP+4]
|
|
MOV ECX,[EAX].TExceptionRecord.ExceptionAddress
|
|
|
|
@@DelphiException:
|
|
{$IFDEF MSWINDOWS}
|
|
CMP BYTE PTR JITEnable,1
|
|
JBE @@GoUnwind
|
|
CMP BYTE PTR DebugHook,0 { Do not JIT if debugging }
|
|
JA @@GoUnwind
|
|
PUSH EAX
|
|
LEA EAX,[ESP+8]
|
|
PUSH EDX
|
|
PUSH ECX
|
|
PUSH EAX
|
|
CALL UnhandledExceptionFilter
|
|
CMP EAX,EXCEPTION_CONTINUE_SEARCH
|
|
POP ECX
|
|
POP EDX
|
|
POP EAX
|
|
JE @@exit
|
|
{$ENDIF}
|
|
|
|
@@GoUnwind:
|
|
OR [EAX].TExceptionRecord.ExceptionFlags,cUnwinding
|
|
|
|
PUSH EBX
|
|
XOR EBX,EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBP
|
|
|
|
MOV EBX,FS:[EBX]
|
|
PUSH EBX { Save pointer to topmost frame }
|
|
PUSH EAX { Save OS exception pointer }
|
|
PUSH EDX { Save exception object }
|
|
PUSH ECX { Save exception address }
|
|
|
|
MOV EDX,[ESP+8+8*4]
|
|
|
|
PUSH 0
|
|
PUSH EAX
|
|
PUSH offset @@returnAddress
|
|
PUSH EDX
|
|
CALL RtlUnwindProc
|
|
@@returnAddress:
|
|
|
|
MOV EDI,[ESP+8+8*4]
|
|
|
|
{ Make the RaiseList entry on the stack }
|
|
|
|
CALL SysInit.@GetTLS
|
|
PUSH [EAX].RaiseListPtr
|
|
MOV [EAX].RaiseListPtr,ESP
|
|
|
|
MOV EBP,[EDI].TExcFrame.hEBP
|
|
MOV EBX,[EDI].TExcFrame.desc
|
|
MOV [EDI].TExcFrame.desc,offset @@exceptFinally
|
|
|
|
ADD EBX,TExcDesc.instructions
|
|
CALL NotifyAnyExcept
|
|
JMP EBX
|
|
|
|
@@exceptFinally:
|
|
JMP _HandleFinally
|
|
|
|
@@destroyExcept:
|
|
{ we come here if an exception handler has thrown yet another exception }
|
|
{ we need to destroy the exception object and pop the raise list. }
|
|
|
|
CALL SysInit.@GetTLS
|
|
MOV ECX,[EAX].RaiseListPtr
|
|
MOV EDX,[ECX].TRaiseFrame.NextRaise
|
|
MOV [EAX].RaiseListPtr,EDX
|
|
|
|
MOV EAX,[ECX].TRaiseFrame.ExceptObject
|
|
JMP TObject.Free
|
|
|
|
@@exit:
|
|
MOV EAX,1
|
|
{$ENDIF} { not PC_MAPPED_EXCEPTIONS }
|
|
end;
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
{
|
|
Common code between the Win32 and PC mapped exception handling
|
|
scheme. This function takes a pointer to an object, and an exception
|
|
'on' descriptor table and finds the matching handler descriptor.
|
|
|
|
For support of Linux, we assume that EBX has been loaded with the GOT
|
|
that pertains to the code which is handling the exception currently.
|
|
If this function is being called from code which is not PIC, then
|
|
EBX should be zero on entry.
|
|
}
|
|
procedure FindOnExceptionDescEntry;
|
|
asm
|
|
{ -> EAX raised object: Pointer }
|
|
{ EDX descriptor table: ^TExcDesc }
|
|
{ EBX GOT of user code, or 0 if not an SO }
|
|
{ <- EAX matching descriptor: ^TExcDescEntry }
|
|
PUSH EBP
|
|
MOV EBP, ESP
|
|
SUB ESP, 8 { Room for vtable temp, and adjustor }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV [EBP - 8], EBX { Store the potential GOT }
|
|
MOV EAX, [EAX] { load vtable of exception object }
|
|
MOV EBX,[EDX].TExcDesc.cnt
|
|
LEA ESI,[EDX].TExcDesc.excTab { point ECX to exc descriptor table }
|
|
MOV [EBP - 4], EAX { temp for vtable of exception object }
|
|
|
|
@@innerLoop:
|
|
MOV EAX,[ESI].TExcDescEntry.vTable
|
|
TEST EAX,EAX { catch all clause? }
|
|
JE @@found { yes: This is the handler }
|
|
ADD EAX, [EBP - 8] { add in the adjustor (could be 0) }
|
|
MOV EDI,[EBP - 4] { load vtable of exception object }
|
|
JMP @@haveVMT
|
|
|
|
@@vtLoop:
|
|
MOV EDI,[EDI]
|
|
@@haveVMT:
|
|
MOV EAX,[EAX]
|
|
CMP EAX,EDI
|
|
JE @@found
|
|
|
|
MOV ECX,[EAX].vmtInstanceSize
|
|
CMP ECX,[EDI].vmtInstanceSize
|
|
JNE @@parent
|
|
|
|
MOV EAX,[EAX].vmtClassName
|
|
MOV EDX,[EDI].vmtClassName
|
|
|
|
XOR ECX,ECX
|
|
MOV CL,[EAX]
|
|
CMP CL,[EDX]
|
|
JNE @@parent
|
|
|
|
INC EAX
|
|
INC EDX
|
|
CALL _AStrCmp
|
|
JE @@found
|
|
|
|
@@parent:
|
|
MOV EDI,[EDI].vmtParent { load vtable of parent }
|
|
MOV EAX,[ESI].TExcDescEntry.vTable
|
|
ADD EAX, [EBP - 8] { add in the adjustor (could be 0) }
|
|
TEST EDI,EDI
|
|
JNE @@vtLoop
|
|
|
|
ADD ESI,8
|
|
DEC EBX
|
|
JNZ @@innerLoop
|
|
|
|
{ Didn't find a handler. }
|
|
XOR ESI, ESI
|
|
|
|
@@found:
|
|
MOV EAX, ESI
|
|
@@done:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
MOV ESP, EBP
|
|
POP EBP
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure _HandleOnExceptionPIC;
|
|
asm
|
|
{ -> EAX obj : Exception object }
|
|
{ [RA] desc: ^TExcDesc }
|
|
{ <- Doesn't return }
|
|
|
|
// Mark the exception as being handled
|
|
OR [EAX].TRaisedException.Flags, excIsBeingHandled
|
|
|
|
MOV ESI, EBX // Save the GOT
|
|
MOV EDX, [ESP] // Get the addr of the TExcDesc
|
|
PUSH EAX // Save the object
|
|
MOV EAX, [EAX].TRaisedException.ExceptObject
|
|
CALL FindOnExceptionDescEntry
|
|
OR EAX, EAX
|
|
JE @@NotForMe
|
|
|
|
MOV EBX, ESI // Set back to user's GOT
|
|
MOV EDX, EAX
|
|
POP EAX // Get the object back
|
|
POP ECX // Ditch the return addr
|
|
|
|
// Get the Pascal object itself.
|
|
MOV EAX, [EAX].TRaisedException.ExceptObject
|
|
|
|
MOV EDX, [EDX].TExcDescEntry.handler
|
|
ADD EDX, EBX // adjust for GOT
|
|
CALL NotifyOnExcept
|
|
|
|
MOV EBX, ESI // Make sure of user's GOT
|
|
JMP EDX // Back to the user code
|
|
// never returns
|
|
@@NotForMe:
|
|
POP EAX // Get the exception object
|
|
|
|
// Mark that we're reraising this exception, so that the
|
|
// compiler generated exception handler for the 'except on' clause
|
|
// will not get confused
|
|
OR [EAX].TRaisedException.Flags, excIsBeingReRaised
|
|
|
|
JMP SysRaiseException // Should be using resume here
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _HandleOnException;
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
asm
|
|
{ -> EAX obj : Exception object }
|
|
{ [RA] desc: ^TExcDesc }
|
|
{ <- Doesn't return }
|
|
|
|
// Mark the exception as being handled
|
|
OR [EAX].TRaisedException.Flags, excIsBeingHandled
|
|
|
|
MOV EDX, [ESP] // Get the addr of the TExcDesc
|
|
PUSH EAX // Save the object
|
|
PUSH EBX // Save EBX
|
|
XOR EBX, EBX // No GOT
|
|
MOV EAX, [EAX].TRaisedException.ExceptObject
|
|
CALL FindOnExceptionDescEntry
|
|
POP EBX // Restore EBX
|
|
OR EAX, EAX // Is the exception for me?
|
|
JE @@NotForMe
|
|
|
|
MOV EDX, EAX
|
|
POP EAX // Get the object back
|
|
POP ECX // Ditch the return addr
|
|
|
|
// Get the Pascal object itself.
|
|
MOV EAX, [EAX].TRaisedException.ExceptObject
|
|
|
|
MOV EDX, [EDX].TExcDescEntry.handler
|
|
CALL NotifyOnExcept // Tell the debugger about it
|
|
|
|
JMP EDX // Back to the user code
|
|
// never returns
|
|
@@NotForMe:
|
|
POP EAX // Get the exception object
|
|
|
|
// Mark that we're reraising this exception, so that the
|
|
// compiler generated exception handler for the 'except on' clause
|
|
// will not get confused
|
|
OR [EAX].TRaisedException.Flags, excIsBeingReRaised
|
|
JMP SysRaiseException // Should be using resume here
|
|
end;
|
|
{$ENDIF}
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
asm
|
|
{ -> [ESP+ 4] excPtr: PExceptionRecord }
|
|
{ [ESP+ 8] errPtr: PExcFrame }
|
|
{ [ESP+12] ctxPtr: Pointer }
|
|
{ [ESP+16] dspPtr: Pointer }
|
|
{ <- EAX return value - always one }
|
|
|
|
MOV EAX,[ESP+4]
|
|
TEST [EAX].TExceptionRecord.ExceptionFlags,cUnwindInProgress
|
|
JNE @@exit
|
|
|
|
CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException
|
|
JE @@DelphiException
|
|
CLD
|
|
CALL _FpuInit
|
|
MOV EDX,ExceptClsProc
|
|
TEST EDX,EDX
|
|
JE @@exit
|
|
CALL EDX
|
|
TEST EAX,EAX
|
|
JNE @@common
|
|
JMP @@exit
|
|
|
|
@@DelphiException:
|
|
MOV EAX,[EAX].TExceptionRecord.ExceptObject
|
|
MOV EAX,[EAX] { load vtable of exception object }
|
|
|
|
@@common:
|
|
|
|
MOV EDX,[ESP+8]
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBP
|
|
|
|
MOV ECX,[EDX].TExcFrame.desc
|
|
MOV EBX,[ECX].TExcDesc.cnt
|
|
LEA ESI,[ECX].TExcDesc.excTab { point ECX to exc descriptor table }
|
|
MOV EBP,EAX { load vtable of exception object }
|
|
|
|
@@innerLoop:
|
|
MOV EAX,[ESI].TExcDescEntry.vTable
|
|
TEST EAX,EAX { catch all clause? }
|
|
JE @@doHandler { yes: go execute handler }
|
|
MOV EDI,EBP { load vtable of exception object }
|
|
JMP @@haveVMT
|
|
|
|
@@vtLoop:
|
|
MOV EDI,[EDI]
|
|
@@haveVMT:
|
|
MOV EAX,[EAX]
|
|
CMP EAX,EDI
|
|
JE @@doHandler
|
|
|
|
MOV ECX,[EAX].vmtInstanceSize
|
|
CMP ECX,[EDI].vmtInstanceSize
|
|
JNE @@parent
|
|
|
|
MOV EAX,[EAX].vmtClassName
|
|
MOV EDX,[EDI].vmtClassName
|
|
|
|
XOR ECX,ECX
|
|
MOV CL,[EAX]
|
|
CMP CL,[EDX]
|
|
JNE @@parent
|
|
|
|
INC EAX
|
|
INC EDX
|
|
CALL _AStrCmp
|
|
JE @@doHandler
|
|
|
|
@@parent:
|
|
MOV EDI,[EDI].vmtParent { load vtable of parent }
|
|
MOV EAX,[ESI].TExcDescEntry.vTable
|
|
TEST EDI,EDI
|
|
JNE @@vtLoop
|
|
|
|
ADD ESI,8
|
|
DEC EBX
|
|
JNZ @@innerLoop
|
|
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
JMP @@exit
|
|
|
|
@@doHandler:
|
|
MOV EAX,[ESP+4+4*4]
|
|
CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException
|
|
MOV EDX,[EAX].TExceptionRecord.ExceptObject
|
|
MOV ECX,[EAX].TExceptionRecord.ExceptAddr
|
|
JE @@haveObject
|
|
CALL ExceptObjProc
|
|
MOV EDX,[ESP+12+4*4]
|
|
CALL NotifyNonDelphiException
|
|
{$IFDEF MSWINDOWS}
|
|
CMP BYTE PTR JITEnable,0
|
|
JBE @@NoJIT
|
|
CMP BYTE PTR DebugHook,0
|
|
JA @@noJIT { Do not JIT if debugging }
|
|
LEA ECX,[ESP+4]
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL UnhandledExceptionFilter
|
|
CMP EAX,EXCEPTION_CONTINUE_SEARCH
|
|
POP EAX
|
|
JE @@exit
|
|
{$ENDIF}
|
|
@@noJIT:
|
|
MOV EDX,EAX
|
|
MOV EAX,[ESP+4+4*4]
|
|
MOV ECX,[EAX].TExceptionRecord.ExceptionAddress
|
|
JMP @@GoUnwind
|
|
|
|
@@haveObject:
|
|
{$IFDEF MSWINDOWS}
|
|
CMP BYTE PTR JITEnable,1
|
|
JBE @@GoUnwind
|
|
CMP BYTE PTR DebugHook,0
|
|
JA @@GoUnwind
|
|
PUSH EAX
|
|
LEA EAX,[ESP+8]
|
|
PUSH EDX
|
|
PUSH ECX
|
|
PUSH EAX
|
|
CALL UnhandledExceptionFilter
|
|
CMP EAX,EXCEPTION_CONTINUE_SEARCH
|
|
POP ECX
|
|
POP EDX
|
|
POP EAX
|
|
JE @@exit
|
|
{$ENDIF}
|
|
|
|
@@GoUnwind:
|
|
XOR EBX,EBX
|
|
MOV EBX,FS:[EBX]
|
|
PUSH EBX { Save topmost frame }
|
|
PUSH EAX { Save exception record }
|
|
PUSH EDX { Save exception object }
|
|
PUSH ECX { Save exception address }
|
|
|
|
MOV EDX,[ESP+8+8*4]
|
|
OR [EAX].TExceptionRecord.ExceptionFlags,cUnwinding
|
|
|
|
PUSH ESI { Save handler entry }
|
|
|
|
PUSH 0
|
|
PUSH EAX
|
|
PUSH offset @@returnAddress
|
|
PUSH EDX
|
|
CALL RtlUnwindProc
|
|
@@returnAddress:
|
|
|
|
POP EBX { Restore handler entry }
|
|
|
|
MOV EDI,[ESP+8+8*4]
|
|
|
|
{ Make the RaiseList entry on the stack }
|
|
|
|
CALL SysInit.@GetTLS
|
|
PUSH [EAX].RaiseListPtr
|
|
MOV [EAX].RaiseListPtr,ESP
|
|
|
|
MOV EBP,[EDI].TExcFrame.hEBP
|
|
MOV [EDI].TExcFrame.desc,offset @@exceptFinally
|
|
MOV EAX,[ESP].TRaiseFrame.ExceptObject
|
|
CALL NotifyOnExcept
|
|
JMP [EBX].TExcDescEntry.handler
|
|
|
|
@@exceptFinally:
|
|
JMP _HandleFinally
|
|
|
|
@@destroyExcept:
|
|
{ we come here if an exception handler has thrown yet another exception }
|
|
{ we need to destroy the exception object and pop the raise list. }
|
|
|
|
CALL SysInit.@GetTLS
|
|
MOV ECX,[EAX].RaiseListPtr
|
|
MOV EDX,[ECX].TRaiseFrame.NextRaise
|
|
MOV [EAX].RaiseListPtr,EDX
|
|
|
|
MOV EAX,[ECX].TRaiseFrame.ExceptObject
|
|
JMP TObject.Free
|
|
@@exit:
|
|
MOV EAX,1
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _HandleFinally;
|
|
asm
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
{$IFDEF PIC}
|
|
MOV ESI, EBX
|
|
{$ENDIF}
|
|
CALL UnblockOSExceptions
|
|
MOV EDX, [ESP]
|
|
CALL NotifyExceptFinally
|
|
PUSH EAX
|
|
{$IFDEF PIC}
|
|
MOV EBX, ESI
|
|
{$ENDIF}
|
|
{
|
|
Mark the current exception with the EBP of the handler. If
|
|
an exception is raised from the finally block, then this
|
|
exception will be orphaned. We will catch this later, when
|
|
we clean up the next except block to complete execution.
|
|
See DoneExcept.
|
|
}
|
|
MOV [EAX].TRaisedException.HandlerEBP, EBP
|
|
CALL EDX
|
|
POP EAX
|
|
{
|
|
We executed the finally handler without adverse reactions.
|
|
It's safe to clear the marker now.
|
|
}
|
|
MOV [EAX].TRaisedException.HandlerEBP, $FFFFFFFF
|
|
PUSH EBP
|
|
MOV EBP, ESP
|
|
CALL SysRaiseException // Should be using resume here
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
{ -> [ESP+ 4] excPtr: PExceptionRecord }
|
|
{ [ESP+ 8] errPtr: PExcFrame }
|
|
{ [ESP+12] ctxPtr: Pointer }
|
|
{ [ESP+16] dspPtr: Pointer }
|
|
{ <- EAX return value - always one }
|
|
|
|
MOV EAX,[ESP+4]
|
|
MOV EDX,[ESP+8]
|
|
TEST [EAX].TExceptionRecord.ExceptionFlags,cUnwindInProgress
|
|
JE @@exit
|
|
MOV ECX,[EDX].TExcFrame.desc
|
|
MOV [EDX].TExcFrame.desc,offset @@exit
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBP
|
|
|
|
MOV EBP,[EDX].TExcFrame.hEBP
|
|
ADD ECX,TExcDesc.instructions
|
|
CALL NotifyExceptFinally
|
|
CALL ECX
|
|
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
|
|
@@exit:
|
|
MOV EAX,1
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
procedure _HandleAutoException;
|
|
{$IFDEF LINUX}
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
asm
|
|
// EAX = TObject reference, or nil
|
|
// [ESP] = ret addr
|
|
|
|
CALL UnblockOSExceptions
|
|
//
|
|
// The compiler wants the stack to look like this:
|
|
// ESP+4-> HRESULT
|
|
// ESP+0-> ret addr
|
|
//
|
|
// Make it so.
|
|
//
|
|
POP EDX
|
|
PUSH 8000FFFFH
|
|
PUSH EDX
|
|
|
|
OR EAX, EAX // Was this a method call?
|
|
JE @@Done
|
|
|
|
PUSH EAX
|
|
CALL CurrentException
|
|
MOV EDX, [EAX].TRaisedException.ExceptObject
|
|
MOV ECX, [EAX].TRaisedException.ExceptionAddr;
|
|
POP EAX
|
|
MOV EAX, [EAX]
|
|
CALL [EAX].vmtSafeCallException.Pointer;
|
|
MOV [ESP+4], EAX
|
|
@@Done:
|
|
CALL _DoneExcept
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Error(reSafeCallError); //!!
|
|
end;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
{ -> [ESP+ 4] excPtr: PExceptionRecord }
|
|
{ [ESP+ 8] errPtr: PExcFrame }
|
|
{ [ESP+12] ctxPtr: Pointer }
|
|
{ [ESP+16] dspPtr: Pointer }
|
|
{ <- EAX return value - always one }
|
|
|
|
MOV EAX,[ESP+4]
|
|
TEST [EAX].TExceptionRecord.ExceptionFlags,cUnwindInProgress
|
|
JNE @@exit
|
|
|
|
CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException
|
|
CLD
|
|
CALL _FpuInit
|
|
JE @@DelphiException
|
|
CMP BYTE PTR JITEnable,0
|
|
JBE @@DelphiException
|
|
CMP BYTE PTR DebugHook,0
|
|
JA @@DelphiException
|
|
|
|
@@DoUnhandled:
|
|
LEA EAX,[ESP+4]
|
|
PUSH EAX
|
|
CALL UnhandledExceptionFilter
|
|
CMP EAX,EXCEPTION_CONTINUE_SEARCH
|
|
JE @@exit
|
|
MOV EAX,[ESP+4]
|
|
JMP @@GoUnwind
|
|
|
|
@@DelphiException:
|
|
CMP BYTE PTR JITEnable,1
|
|
JBE @@GoUnwind
|
|
CMP BYTE PTR DebugHook,0
|
|
JA @@GoUnwind
|
|
JMP @@DoUnhandled
|
|
|
|
@@GoUnwind:
|
|
OR [EAX].TExceptionRecord.ExceptionFlags,cUnwinding
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBP
|
|
|
|
MOV EDX,[ESP+8+3*4]
|
|
|
|
PUSH 0
|
|
PUSH EAX
|
|
PUSH offset @@returnAddress
|
|
PUSH EDX
|
|
CALL RtlUnwindProc
|
|
|
|
@@returnAddress:
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
MOV EAX,[ESP+4]
|
|
MOV EBX,8000FFFFH
|
|
CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException
|
|
JNE @@done
|
|
|
|
MOV EDX,[EAX].TExceptionRecord.ExceptObject
|
|
MOV ECX,[EAX].TExceptionRecord.ExceptAddr
|
|
MOV EAX,[ESP+8]
|
|
MOV EAX,[EAX].TExcFrame.SelfOfMethod
|
|
TEST EAX,EAX
|
|
JZ @@freeException
|
|
MOV EBX,[EAX]
|
|
CALL [EBX].vmtSafeCallException.Pointer
|
|
MOV EBX,EAX
|
|
@@freeException:
|
|
MOV EAX,[ESP+4]
|
|
MOV EAX,[EAX].TExceptionRecord.ExceptObject
|
|
CALL TObject.Free
|
|
@@done:
|
|
XOR EAX,EAX
|
|
MOV ESP,[ESP+8]
|
|
POP ECX
|
|
MOV FS:[EAX],ECX
|
|
POP EDX
|
|
POP EBP
|
|
LEA EDX,[EDX].TExcDesc.instructions
|
|
POP ECX
|
|
JMP EDX
|
|
@@exit:
|
|
MOV EAX,1
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure _RaiseAtExcept;
|
|
asm
|
|
{ -> EAX Pointer to exception object }
|
|
{ -> EDX Purported addr of exception }
|
|
{ Be careful: EBX is not set up in PIC mode. }
|
|
{ Outward bound calls must go through an exported fn, like SysRaiseException }
|
|
OR EAX, EAX
|
|
JNE @@GoAhead
|
|
MOV EAX, 216
|
|
CALL _RunError
|
|
|
|
@@GoAhead:
|
|
CALL BlockOSExceptions
|
|
PUSH EBP
|
|
MOV EBP, ESP
|
|
CALL NotifyReRaise
|
|
CALL AllocateException
|
|
CALL SysRaiseException
|
|
{
|
|
This can only return if there was a terrible error. In this event,
|
|
we have to bail out.
|
|
}
|
|
JMP _Run0Error
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _RaiseExcept;
|
|
asm
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
{ -> EAX Pointer to exception object }
|
|
MOV EDX, [ESP]
|
|
JMP _RaiseAtExcept
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
{ When making changes to the way Delphi Exceptions are raised, }
|
|
{ please realize that the C++ Exception handling code reraises }
|
|
{ some exceptions as Delphi Exceptions. Of course we want to }
|
|
{ keep exception raising compatible between Delphi and C++, so }
|
|
{ when you make changes here, consult with the relevant C++ }
|
|
{ exception handling engineer. The C++ code is in xx.cpp, in }
|
|
{ the RTL sources, in function tossAnException. }
|
|
|
|
{ -> EAX Pointer to exception object }
|
|
{ [ESP] Error address }
|
|
|
|
OR EAX, EAX
|
|
JNE @@GoAhead
|
|
MOV EAX, 216
|
|
CALL _RunError
|
|
@@GoAhead:
|
|
POP EDX
|
|
|
|
PUSH ESP
|
|
PUSH EBP
|
|
PUSH EDI
|
|
PUSH ESI
|
|
PUSH EBX
|
|
PUSH EAX { pass class argument }
|
|
PUSH EDX { pass address argument }
|
|
|
|
PUSH ESP { pass pointer to arguments }
|
|
PUSH 7 { there are seven arguments }
|
|
PUSH cNonContinuable { we can't continue execution }
|
|
PUSH cDelphiException { our magic exception code }
|
|
PUSH EDX { pass the user's return address }
|
|
JMP RaiseExceptionProc
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
{
|
|
Used in the PC mapping exception implementation to handle exceptions in constructors.
|
|
}
|
|
procedure _ClassHandleException;
|
|
asm
|
|
{
|
|
EAX = self
|
|
EDX = top flag
|
|
}
|
|
TEST DL, DL
|
|
JE _RaiseAgain
|
|
MOV ECX,[EAX]
|
|
MOV DL,$81
|
|
PUSH EAX
|
|
CALL dword ptr [ECX].vmtDestroy
|
|
POP EAX
|
|
CALL _ClassDestroy
|
|
JMP _RaiseAgain
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _RaiseAgain;
|
|
asm
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
CALL CurrentException
|
|
// The following notifies the debugger of a reraise of exceptions. This will
|
|
// be supported in a later release, but is disabled for now.
|
|
// PUSH EAX
|
|
// MOV EDX, [EAX].TRaisedException.ExceptionAddr
|
|
// MOV EAX, [EAX].TRaisedException.ExceptObject
|
|
// CALL NotifyReRaise { Tell the debugger }
|
|
// POP EAX
|
|
TEST [EAX].TRaisedException.Flags, excIsBeingHandled
|
|
JZ @@DoIt
|
|
OR [EAX].TRaisedException.Flags, excIsBeingReRaised
|
|
@@DoIt:
|
|
MOV EDX, [ESP] { Get the user's addr }
|
|
JMP SysRaiseException
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
{ -> [ESP ] return address to user program }
|
|
{ [ESP+ 4 ] raise list entry (4 dwords) }
|
|
{ [ESP+ 4+ 4*4] saved topmost frame }
|
|
{ [ESP+ 4+ 5*4] saved registers (4 dwords) }
|
|
{ [ESP+ 4+ 9*4] return address to OS }
|
|
{ -> [ESP+ 4+10*4] excPtr: PExceptionRecord }
|
|
{ [ESP+ 8+10*4] errPtr: PExcFrame }
|
|
|
|
{ Point the error handler of the exception frame to something harmless }
|
|
|
|
MOV EAX,[ESP+8+10*4]
|
|
MOV [EAX].TExcFrame.desc,offset @@exit
|
|
|
|
{ Pop the RaiseList }
|
|
|
|
CALL SysInit.@GetTLS
|
|
MOV EDX,[EAX].RaiseListPtr
|
|
MOV ECX,[EDX].TRaiseFrame.NextRaise
|
|
MOV [EAX].RaiseListPtr,ECX
|
|
|
|
{ Destroy any objects created for non-delphi exceptions }
|
|
|
|
MOV EAX,[EDX].TRaiseFrame.ExceptionRecord
|
|
AND [EAX].TExceptionRecord.ExceptionFlags,NOT cUnwinding
|
|
CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException
|
|
JE @@delphiException
|
|
MOV EAX,[EDX].TRaiseFrame.ExceptObject
|
|
CALL TObject.Free
|
|
CALL NotifyReRaise
|
|
|
|
@@delphiException:
|
|
|
|
XOR EAX,EAX
|
|
ADD ESP,5*4
|
|
MOV EDX,FS:[EAX]
|
|
POP ECX
|
|
MOV EDX,[EDX].TExcFrame.next
|
|
MOV [ECX].TExcFrame.next,EDX
|
|
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
@@exit:
|
|
MOV EAX,1
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFDEF DEBUG_EXCEPTIONS}
|
|
procedure NoteDE;
|
|
begin
|
|
Writeln('DoneExcept: Skipped the destructor');
|
|
end;
|
|
|
|
procedure NoteDE2;
|
|
begin
|
|
Writeln('DoneExcept: Destroyed the object');
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
{
|
|
This is implemented slow and dumb. The theory is that it is rare
|
|
to throw an exception past an except handler, and that the penalty
|
|
can be particularly high here. Partly it's done the dumb way for
|
|
the sake of maintainability. It could be inlined.
|
|
}
|
|
procedure _DestroyException;
|
|
var
|
|
Exc: PRaisedException;
|
|
RefCount: Integer;
|
|
ExcObj: Pointer;
|
|
ExcAddr: Pointer;
|
|
begin
|
|
asm
|
|
MOV Exc, EAX
|
|
end;
|
|
|
|
if (Exc^.Flags and excIsBeingReRaised) = 0 then
|
|
begin
|
|
RefCount := Exc^.RefCount;
|
|
ExcObj := Exc^.ExceptObject;
|
|
ExcAddr := Exc^.ExceptionAddr;
|
|
Exc^.RefCount := 1;
|
|
FreeException;
|
|
_DoneExcept;
|
|
Exc := AllocateException(ExcObj, ExcAddr);
|
|
Exc^.RefCount := RefCount;
|
|
end;
|
|
|
|
Exc^.Flags := Exc^.Flags and not (excIsBeingReRaised or excIsBeingHandled);
|
|
|
|
SysRaiseException(Exc);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _DoneExcept;
|
|
asm
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
CALL FreeException
|
|
OR EAX, EAX
|
|
JE @@Done
|
|
CALL TObject.Free
|
|
@@Done:
|
|
{
|
|
Take a peek at the next exception object on the stack.
|
|
If its EBP marker is at an address lower than our current
|
|
EBP, then we know that it was orphaned when an exception was
|
|
thrown from within the execution of a finally block. We clean
|
|
it up now, so that we won't leak exception records/objects.
|
|
}
|
|
CALL CurrentException
|
|
OR EAX, EAX
|
|
JE @@Done2
|
|
CMP [EAX].TRaisedException.HandlerEBP, EBP
|
|
JA @@Done2
|
|
CALL FreeException
|
|
OR EAX, EAX
|
|
JE @@Done2
|
|
CALL TObject.Free
|
|
@@Done2:
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
{ -> [ESP+ 4+10*4] excPtr: PExceptionRecord }
|
|
{ [ESP+ 8+10*4] errPtr: PExcFrame }
|
|
|
|
{ Pop the RaiseList }
|
|
|
|
CALL SysInit.@GetTLS
|
|
MOV EDX,[EAX].RaiseListPtr
|
|
MOV ECX,[EDX].TRaiseFrame.NextRaise
|
|
MOV [EAX].RaiseListPtr,ECX
|
|
|
|
{ Destroy exception object }
|
|
|
|
MOV EAX,[EDX].TRaiseFrame.ExceptObject
|
|
CALL TObject.Free
|
|
|
|
POP EDX
|
|
MOV ESP,[ESP+8+9*4]
|
|
XOR EAX,EAX
|
|
POP ECX
|
|
MOV FS:[EAX],ECX
|
|
POP EAX
|
|
POP EBP
|
|
CALL NotifyTerminate
|
|
JMP EDX
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure _TryFinallyExit;
|
|
asm
|
|
{$IFDEF MSWINDOWS}
|
|
XOR EDX,EDX
|
|
MOV ECX,[ESP+4].TExcFrame.desc
|
|
MOV EAX,[ESP+4].TExcFrame.next
|
|
ADD ECX,TExcDesc.instructions
|
|
MOV FS:[EDX],EAX
|
|
CALL ECX
|
|
@@1: RET 12
|
|
{$ENDIF}
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
var
|
|
InitContext: TInitContext;
|
|
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
procedure MapToRunError(P: PExceptionRecord); stdcall;
|
|
const
|
|
STATUS_ACCESS_VIOLATION = $C0000005;
|
|
STATUS_ARRAY_BOUNDS_EXCEEDED = $C000008C;
|
|
STATUS_FLOAT_DENORMAL_OPERAND = $C000008D;
|
|
STATUS_FLOAT_DIVIDE_BY_ZERO = $C000008E;
|
|
STATUS_FLOAT_INEXACT_RESULT = $C000008F;
|
|
STATUS_FLOAT_INVALID_OPERATION = $C0000090;
|
|
STATUS_FLOAT_OVERFLOW = $C0000091;
|
|
STATUS_FLOAT_STACK_CHECK = $C0000092;
|
|
STATUS_FLOAT_UNDERFLOW = $C0000093;
|
|
STATUS_INTEGER_DIVIDE_BY_ZERO = $C0000094;
|
|
STATUS_INTEGER_OVERFLOW = $C0000095;
|
|
STATUS_PRIVILEGED_INSTRUCTION = $C0000096;
|
|
STATUS_STACK_OVERFLOW = $C00000FD;
|
|
STATUS_CONTROL_C_EXIT = $C000013A;
|
|
var
|
|
ErrCode: Byte;
|
|
begin
|
|
case P.ExceptionCode of
|
|
STATUS_INTEGER_DIVIDE_BY_ZERO: ErrCode := 200;
|
|
STATUS_ARRAY_BOUNDS_EXCEEDED: ErrCode := 201;
|
|
STATUS_FLOAT_OVERFLOW: ErrCode := 205;
|
|
STATUS_FLOAT_INEXACT_RESULT,
|
|
STATUS_FLOAT_INVALID_OPERATION,
|
|
STATUS_FLOAT_STACK_CHECK: ErrCode := 207;
|
|
STATUS_FLOAT_DIVIDE_BY_ZERO: ErrCode := 200;
|
|
STATUS_INTEGER_OVERFLOW: ErrCode := 215;
|
|
STATUS_FLOAT_UNDERFLOW,
|
|
STATUS_FLOAT_DENORMAL_OPERAND: ErrCode := 206;
|
|
STATUS_ACCESS_VIOLATION: ErrCode := 216;
|
|
STATUS_PRIVILEGED_INSTRUCTION: ErrCode := 218;
|
|
STATUS_CONTROL_C_EXIT: ErrCode := 217;
|
|
STATUS_STACK_OVERFLOW: ErrCode := 202;
|
|
else ErrCode := 255;
|
|
end;
|
|
RunErrorAt(ErrCode, P.ExceptionAddress);
|
|
end;
|
|
|
|
|
|
procedure _ExceptionHandler;
|
|
asm
|
|
MOV EAX,[ESP+4]
|
|
|
|
TEST [EAX].TExceptionRecord.ExceptionFlags,cUnwindInProgress
|
|
JNE @@exit
|
|
{$IFDEF MSWINDOWS}
|
|
CMP BYTE PTR DebugHook,0
|
|
JA @@ExecuteHandler
|
|
LEA EAX,[ESP+4]
|
|
PUSH EAX
|
|
CALL UnhandledExceptionFilter
|
|
CMP EAX,EXCEPTION_CONTINUE_SEARCH
|
|
JNE @@ExecuteHandler
|
|
JMP @@exit
|
|
{$ENDIF}
|
|
|
|
@@ExecuteHandler:
|
|
MOV EAX,[ESP+4]
|
|
CLD
|
|
CALL _FpuInit
|
|
MOV EDX,[ESP+8]
|
|
|
|
PUSH 0
|
|
PUSH EAX
|
|
PUSH offset @@returnAddress
|
|
PUSH EDX
|
|
CALL RtlUnwindProc
|
|
|
|
@@returnAddress:
|
|
MOV EBX,[ESP+4]
|
|
CMP [EBX].TExceptionRecord.ExceptionCode,cDelphiException
|
|
MOV EDX,[EBX].TExceptionRecord.ExceptAddr
|
|
MOV EAX,[EBX].TExceptionRecord.ExceptObject
|
|
JE @@DelphiException2
|
|
|
|
MOV EDX,ExceptObjProc
|
|
TEST EDX,EDX
|
|
JE MapToRunError
|
|
MOV EAX,EBX
|
|
CALL EDX
|
|
TEST EAX,EAX
|
|
JE MapToRunError
|
|
MOV EDX,[EBX].TExceptionRecord.ExceptionAddress
|
|
|
|
@@DelphiException2:
|
|
|
|
CALL NotifyUnhandled
|
|
MOV ECX,ExceptProc
|
|
TEST ECX,ECX
|
|
JE @@noExceptProc
|
|
CALL ECX { call ExceptProc(ExceptObject, ExceptAddr) }
|
|
|
|
@@noExceptProc:
|
|
MOV ECX,[ESP+4]
|
|
MOV EAX,217
|
|
MOV EDX,[ECX].TExceptionRecord.ExceptAddr
|
|
MOV [ESP],EDX
|
|
JMP _RunError
|
|
|
|
@@exit:
|
|
XOR EAX,EAX
|
|
end;
|
|
|
|
procedure SetExceptionHandler;
|
|
asm
|
|
XOR EDX,EDX { using [EDX] saves some space over [0] }
|
|
{X} // now we come here from another place, and EBP is used above for loop counter
|
|
{X} // let us restore it...
|
|
{X} PUSH EBP
|
|
{X} LEA EBP, [ESP + $50]
|
|
LEA EAX,[EBP-12]
|
|
MOV ECX,FS:[EDX] { ECX := head of chain }
|
|
MOV FS:[EDX],EAX { head of chain := @exRegRec }
|
|
|
|
MOV [EAX].TExcFrame.next,ECX
|
|
{$IFDEF PIC}
|
|
LEA EDX, [EBX]._ExceptionHandler
|
|
MOV [EAX].TExcFrame.desc, EDX
|
|
{$ELSE}
|
|
MOV [EAX].TExcFrame.desc,offset _ExceptionHandler
|
|
{$ENDIF}
|
|
MOV [EAX].TExcFrame.hEBP,EBP
|
|
{$IFDEF PIC}
|
|
MOV [EBX].InitContext.ExcFrame,EAX
|
|
{$ELSE}
|
|
MOV InitContext.ExcFrame,EAX
|
|
{$ENDIF}
|
|
|
|
{X} POP EBP
|
|
end;
|
|
|
|
|
|
procedure UnsetExceptionHandler;
|
|
asm
|
|
XOR EDX,EDX
|
|
{$IFDEF PIC}
|
|
MOV EAX,[EBX].InitContext.ExcFrame
|
|
{$ELSE}
|
|
MOV EAX,InitContext.ExcFrame
|
|
{$ENDIF}
|
|
TEST EAX,EAX
|
|
JZ @@exit
|
|
MOV ECX,FS:[EDX] { ECX := head of chain }
|
|
CMP EAX,ECX { simple case: our record is first }
|
|
JNE @@search
|
|
MOV EAX,[EAX] { head of chain := exRegRec.next }
|
|
MOV FS:[EDX],EAX
|
|
JMP @@exit
|
|
|
|
@@loop:
|
|
MOV ECX,[ECX]
|
|
@@search:
|
|
CMP ECX,-1 { at end of list? }
|
|
JE @@exit { yes - didn't find it }
|
|
CMP [ECX],EAX { is it the next one on the list? }
|
|
JNE @@loop { no - look at next one on list }
|
|
@@unlink: { yes - unlink our record }
|
|
MOV EAX,[EAX] { get next record on list }
|
|
MOV [ECX],EAX { unlink our record }
|
|
@@exit:
|
|
end;
|
|
{$ENDIF} // not PC_MAPPED_EXCEPTIONS
|
|
|
|
type
|
|
TProc = procedure;
|
|
|
|
{$IFDEF LINUX}
|
|
procedure CallProc(Proc: Pointer; GOT: Cardinal);
|
|
asm
|
|
PUSH EBX
|
|
MOV EBX,EDX
|
|
ADD EAX,EBX
|
|
CALL EAX
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
(*X- Original version... discarded
|
|
procedure FinalizeUnits;
|
|
var
|
|
Count: Integer;
|
|
Table: PUnitEntryTable;
|
|
P: Pointer;
|
|
begin
|
|
if InitContext.InitTable = nil then
|
|
exit;
|
|
Count := InitContext.InitCount;
|
|
Table := InitContext.InitTable^.UnitInfo;
|
|
{$IFDEF LINUX}
|
|
Inc(Cardinal(Table), InitContext.Module^.GOT);
|
|
{$ENDIF}
|
|
try
|
|
while Count > 0 do
|
|
begin
|
|
Dec(Count);
|
|
InitContext.InitCount := Count;
|
|
P := Table^[Count].FInit;
|
|
if Assigned(P) then
|
|
begin
|
|
{$IFDEF LINUX}
|
|
CallProc(P, InitContext.Module^.GOT);
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
TProc(P)();
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
except
|
|
FinalizeUnits; { try to finalize the others }
|
|
raise;
|
|
end;
|
|
end;
|
|
X+*)
|
|
|
|
{X+ see comments in InitUnits below }
|
|
//procedure FInitUnits; {X} - renamed to FInitUnitsHard
|
|
{X} procedure FInitUnitsHard;
|
|
var
|
|
Count: Integer;
|
|
Table: PUnitEntryTable;
|
|
P: procedure;
|
|
begin
|
|
if InitContext.InitTable = nil then
|
|
exit;
|
|
Count := InitContext.InitCount;
|
|
Table := InitContext.InitTable^.UnitInfo;
|
|
{$IFDEF LINUX}
|
|
Inc(Cardinal(Table), InitContext.Module^.GOT);
|
|
{$ENDIF}
|
|
try
|
|
while Count > 0 do
|
|
begin
|
|
Dec(Count);
|
|
InitContext.InitCount := Count;
|
|
P := Table^[Count].FInit;
|
|
if Assigned(P) then
|
|
{$IFDEF LINUX}
|
|
CallProc(P, InitContext.Module^.GOT);
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
TProc(P)();
|
|
{$ENDIF}
|
|
end;
|
|
except
|
|
{X- rename: FInitUnits; { try to finalize the others }
|
|
FInitUnitsHard;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
// This handler can be set in initialization section of
|
|
// unit SysSfIni.pas only.
|
|
procedure InitUnitsHard( Table : PUnitEntryTable; Idx, Count : Integer );
|
|
begin
|
|
try
|
|
InitUnitsLight( Table, Idx, Count );
|
|
except
|
|
FInitUnitsHard;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
{X+ see comments in InitUnits below }
|
|
procedure FInitUnitsLight;
|
|
var
|
|
Count: Integer;
|
|
Table: PUnitEntryTable;
|
|
P: procedure;
|
|
begin
|
|
if InitContext.InitTable = nil then
|
|
exit;
|
|
Count := InitContext.InitCount;
|
|
Table := InitContext.InitTable^.UnitInfo;
|
|
{$IFDEF LINUX}
|
|
Inc(Cardinal(Table), InitContext.Module^.GOT);
|
|
{$ENDIF}
|
|
while Count > 0 do
|
|
begin
|
|
Dec(Count);
|
|
InitContext.InitCount := Count;
|
|
P := Table^[Count].FInit;
|
|
if Assigned(P) then
|
|
{$IFDEF LINUX}
|
|
CallProc(P, InitContext.Module^.GOT);
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
TProc(P)();
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
{X+ see comments in InitUnits below }
|
|
procedure InitUnitsLight( Table : PUnitEntryTable; Idx, Count : Integer );
|
|
var P : procedure;
|
|
Light : Boolean;
|
|
begin
|
|
Light := @InitUnitsProc = @InitUnitsLight;
|
|
while Idx < Count do
|
|
begin
|
|
P := Table^[ Idx ].Init;
|
|
Inc( Idx );
|
|
InitContext.InitCount := Idx;
|
|
if Assigned( P ) then
|
|
P;
|
|
if Light and (@InitUnitsProc <> @InitUnitsLight) then
|
|
begin
|
|
InitUnitsProc( Table, Idx, Count );
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{X+ see comments in body of InitUnits below }
|
|
procedure InitUnits;
|
|
var
|
|
Count, I: Integer;
|
|
Table: PUnitEntryTable;
|
|
{X- P: Pointer; }
|
|
begin
|
|
if InitContext.InitTable = nil then
|
|
exit;
|
|
Count := InitContext.InitTable^.UnitCount;
|
|
I := 0;
|
|
Table := InitContext.InitTable^.UnitInfo;
|
|
{$IFDEF LINUX}
|
|
Inc(Cardinal(Table), InitContext.Module^.GOT);
|
|
{$ENDIF}
|
|
(*X- by default, Delphi InitUnits uses try-except & raise constructions,
|
|
which leads to permanent use of all exception handler routines.
|
|
Let us make this by another way.
|
|
try
|
|
while I < Count do
|
|
begin
|
|
P := Table^[I].Init;
|
|
Inc(I);
|
|
InitContext.InitCount := I;
|
|
if Assigned(P) then
|
|
begin
|
|
{$IFDEF LINUX}
|
|
CallProc(P, InitContext.Module^.GOT);
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
TProc(P)();
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
except
|
|
FinalizeUnits;
|
|
raise;
|
|
end;
|
|
X+*)
|
|
InitUnitsProc( Table, I, Count ); //{X}
|
|
end;
|
|
|
|
procedure _PackageLoad(const Table : PackageInfo; Module: PLibModule);
|
|
var
|
|
SavedContext: TInitContext;
|
|
begin
|
|
SavedContext := InitContext;
|
|
InitContext.DLLInitState := 0;
|
|
InitContext.InitTable := Table;
|
|
InitContext.InitCount := 0;
|
|
InitContext.Module := Module;
|
|
InitContext.OuterContext := @SavedContext;
|
|
try
|
|
InitUnits;
|
|
finally
|
|
InitContext := SavedContext;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure _PackageUnload(const Table : PackageInfo; Module: PLibModule);
|
|
var
|
|
SavedContext: TInitContext;
|
|
begin
|
|
SavedContext := InitContext;
|
|
InitContext.DLLInitState := 0;
|
|
InitContext.InitTable := Table;
|
|
InitContext.InitCount := Table^.UnitCount;
|
|
InitContext.Module := Module;
|
|
InitContext.OuterContext := @SavedContext;
|
|
try
|
|
{X} //FinalizeUnits;
|
|
FInitUnitsProc;
|
|
finally
|
|
InitContext := SavedContext;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF LINUX}
|
|
procedure _StartExe(InitTable: PackageInfo; Module: PLibModule; Argc: Integer; Argv: Pointer);
|
|
begin
|
|
ArgCount := Argc;
|
|
ArgValues := Argv;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
procedure _StartExe(InitTable: PackageInfo; Module: PLibModule);
|
|
begin
|
|
RaiseExceptionProc := @RaiseException;
|
|
RTLUnwindProc := @RTLUnwind;
|
|
{$ENDIF}
|
|
InitContext.InitTable := InitTable;
|
|
InitContext.InitCount := 0;
|
|
InitContext.Module := Module;
|
|
MainInstance := Module.Instance;
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
|
|
{X SetExceptionHandler; - moved to SysSfIni.pas }
|
|
|
|
{$ENDIF}
|
|
IsLibrary := False;
|
|
InitUnits;
|
|
end;
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
procedure _StartLib;
|
|
asm
|
|
{ -> EAX InitTable }
|
|
{ EDX Module }
|
|
{ ECX InitTLS }
|
|
{ [ESP+4] DllProc }
|
|
{ [EBP+8] HInst }
|
|
{ [EBP+12] Reason }
|
|
|
|
{ Push some desperately needed registers }
|
|
|
|
PUSH ECX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
{ Save the current init context into the stackframe of our caller }
|
|
|
|
MOV ESI,offset InitContext
|
|
LEA EDI,[EBP- (type TExcFrame) - (type TInitContext)]
|
|
MOV ECX,(type TInitContext)/4
|
|
REP MOVSD
|
|
|
|
{ Setup the current InitContext }
|
|
|
|
POP InitContext.DLLSaveEDI
|
|
POP InitContext.DLLSaveESI
|
|
MOV InitContext.DLLSaveEBP,EBP
|
|
MOV InitContext.DLLSaveEBX,EBX
|
|
MOV InitContext.InitTable,EAX
|
|
MOV InitContext.Module,EDX
|
|
LEA ECX,[EBP- (type TExcFrame) - (type TInitContext)]
|
|
MOV InitContext.OuterContext,ECX
|
|
XOR ECX,ECX
|
|
CMP dword ptr [EBP+12],0
|
|
JNE @@notShutDown
|
|
MOV ECX,[EAX].PackageInfoTable.UnitCount
|
|
@@notShutDown:
|
|
MOV InitContext.InitCount,ECX
|
|
|
|
MOV EAX, offset RaiseException
|
|
MOV RaiseExceptionProc, EAX
|
|
MOV EAX, offset RTLUnwind
|
|
MOV RTLUnwindProc, EAX
|
|
|
|
CALL SetExceptionHandler
|
|
|
|
MOV EAX,[EBP+12]
|
|
INC EAX
|
|
MOV InitContext.DLLInitState,AL
|
|
DEC EAX
|
|
|
|
{ Init any needed TLS }
|
|
|
|
POP ECX
|
|
MOV EDX,[ECX]
|
|
MOV InitContext.ExitProcessTLS,EDX
|
|
JE @@skipTLSproc
|
|
CMP AL,3 // DLL_THREAD_DETACH
|
|
JGE @@skipTLSproc // call ExitThreadTLS proc after DLLProc
|
|
CALL dword ptr [ECX+EAX*4]
|
|
@@skipTLSproc:
|
|
|
|
{ Call any DllProc }
|
|
|
|
PUSH ECX
|
|
MOV ECX,[ESP+4]
|
|
TEST ECX,ECX
|
|
JE @@noDllProc
|
|
MOV EAX,[EBP+12]
|
|
MOV EDX,[EBP+16]
|
|
CALL ECX
|
|
@@noDllProc:
|
|
|
|
POP ECX
|
|
MOV EAX, [EBP+12]
|
|
CMP AL,3 // DLL_THREAD_DETACH
|
|
JL @@afterDLLproc // don't free TLS on process shutdown
|
|
CALL dword ptr [ECX+EAX*4]
|
|
|
|
@@afterDLLProc:
|
|
|
|
{ Set IsLibrary if there was no exe yet }
|
|
|
|
CMP MainInstance,0
|
|
JNE @@haveExe
|
|
MOV IsLibrary,1
|
|
FNSTCW Default8087CW // save host exe's FPU preferences
|
|
|
|
@@haveExe:
|
|
|
|
MOV EAX,[EBP+12]
|
|
DEC EAX
|
|
JNE _Halt0
|
|
CALL InitUnits
|
|
RET 4
|
|
end;
|
|
{$ENDIF MSWINDOWS}
|
|
{$IFDEF LINUX}
|
|
procedure _StartLib(Context: PInitContext; Module: PLibModule; DLLProc: TDLLProcEx);
|
|
var
|
|
TempSwap: TInitContext;
|
|
begin
|
|
// Context's register save fields are already initialized.
|
|
// Save the current InitContext and activate the new Context by swapping them
|
|
TempSwap := InitContext;
|
|
InitContext := PInitContext(Context)^;
|
|
PInitContext(Context)^ := TempSwap;
|
|
|
|
InitContext.Module := Module;
|
|
InitContext.OuterContext := Context;
|
|
|
|
// DLLInitState is initialized by SysInit to 0 for shutdown, 1 for startup
|
|
// Inc DLLInitState to distinguish from package init:
|
|
// 0 for package, 1 for DLL shutdown, 2 for DLL startup
|
|
|
|
Inc(InitContext.DLLInitState);
|
|
|
|
if InitContext.DLLInitState = 1 then
|
|
begin
|
|
InitContext.InitTable := Module.InitTable;
|
|
if Assigned(InitContext.InitTable) then
|
|
InitContext.InitCount := InitContext.InitTable.UnitCount // shutdown
|
|
end
|
|
else
|
|
begin
|
|
Module.InitTable := InitContext.InitTable; // save for shutdown
|
|
InitContext.InitCount := 0; // startup
|
|
end;
|
|
|
|
if Assigned(DLLProc) then
|
|
DLLProc(InitContext.DLLInitState-1,0);
|
|
|
|
if MainInstance = 0 then { Set IsLibrary if there was no exe yet }
|
|
begin
|
|
IsLibrary := True;
|
|
Default8087CW := Get8087CW;
|
|
end;
|
|
|
|
if InitContext.DLLInitState = 1 then
|
|
_Halt0
|
|
else
|
|
InitUnits;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _InitResStrings;
|
|
asm
|
|
{ -> EAX Pointer to init table }
|
|
{ record }
|
|
{ cnt: Integer; }
|
|
{ tab: array [1..cnt] record }
|
|
{ variableAddress: Pointer; }
|
|
{ resStringAddress: Pointer; }
|
|
{ end; }
|
|
{ end; }
|
|
{ EBX = caller's GOT for PIC callers, 0 for non-PIC }
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
PUSH EBX
|
|
XOR EBX,EBX
|
|
{$ENDIF}
|
|
PUSH EDI
|
|
PUSH ESI
|
|
MOV EDI,[EBX+EAX]
|
|
LEA ESI,[EBX+EAX+4]
|
|
@@loop:
|
|
MOV EAX,[ESI+4] { load resStringAddress }
|
|
MOV EDX,[ESI] { load variableAddress }
|
|
ADD EAX,EBX
|
|
ADD EDX,EBX
|
|
CALL LoadResString
|
|
ADD ESI,8
|
|
DEC EDI
|
|
JNZ @@loop
|
|
|
|
POP ESI
|
|
POP EDI
|
|
{$IFDEF MSWINDOWS}
|
|
POP EBX
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _InitResStringImports;
|
|
asm
|
|
{ -> EAX Pointer to init table }
|
|
{ record }
|
|
{ cnt: Integer; }
|
|
{ tab: array [1..cnt] record }
|
|
{ variableAddress: Pointer; }
|
|
{ resStringAddress: ^Pointer; }
|
|
{ end; }
|
|
{ end; }
|
|
{ EBX = caller's GOT for PIC callers, 0 for non-PIC }
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
PUSH EBX
|
|
XOR EBX,EBX
|
|
{$ENDIF}
|
|
PUSH EDI
|
|
PUSH ESI
|
|
MOV EDI,[EBX+EAX]
|
|
LEA ESI,[EBX+EAX+4]
|
|
@@loop:
|
|
MOV EAX,[ESI+4] { load address of import }
|
|
MOV EDX,[ESI] { load address of variable }
|
|
MOV EAX,[EBX+EAX] { load contents of import }
|
|
ADD EDX,EBX
|
|
CALL LoadResString
|
|
ADD ESI,8
|
|
DEC EDI
|
|
JNZ @@loop
|
|
|
|
POP ESI
|
|
POP EDI
|
|
{$IFDEF MSWINDOWS}
|
|
POP EBX
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _InitImports;
|
|
asm
|
|
{ -> EAX Pointer to init table }
|
|
{ record }
|
|
{ cnt: Integer; }
|
|
{ tab: array [1..cnt] record }
|
|
{ variableAddress: Pointer; }
|
|
{ sourceAddress: ^Pointer; }
|
|
{ sourceOffset: Longint; }
|
|
{ end; }
|
|
{ end; }
|
|
{ EBX = caller's GOT for PIC callers, 0 for non-PIC }
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
PUSH EBX
|
|
XOR EBX,EBX
|
|
{$ENDIF}
|
|
PUSH EDI
|
|
PUSH ESI
|
|
MOV EDI,[EBX+EAX]
|
|
LEA ESI,[EBX+EAX+4]
|
|
@@loop:
|
|
MOV EAX,[ESI+4] { load address of import }
|
|
MOV EDX,[ESI] { load address of variable }
|
|
MOV EAX,[EBX+EAX] { load contents of import }
|
|
ADD EAX,[ESI+8] { calc address of variable }
|
|
MOV [EBX+EDX],EAX { store result }
|
|
ADD ESI,12
|
|
DEC EDI
|
|
JNZ @@loop
|
|
|
|
POP ESI
|
|
POP EDI
|
|
{$IFDEF MSWINDOWS}
|
|
POP EBX
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
procedure _InitWideStrings;
|
|
asm
|
|
{ -> EAX Pointer to init table }
|
|
{ record }
|
|
{ cnt: Integer; }
|
|
{ tab: array [1..cnt] record }
|
|
{ variableAddress: Pointer; }
|
|
{ stringAddress: ^Pointer; }
|
|
{ end; }
|
|
{ end; }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV EBX,[EAX]
|
|
LEA ESI,[EAX+4]
|
|
@@loop:
|
|
MOV EDX,[ESI+4] { load address of string }
|
|
MOV EAX,[ESI] { load address of variable }
|
|
CALL _WStrAsg
|
|
ADD ESI,8
|
|
DEC EBX
|
|
JNZ @@loop
|
|
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
var
|
|
runErrMsg: array[0..29] of Char = 'Runtime error at 00000000'#0;
|
|
// columns: 0123456789012345678901234567890
|
|
errCaption: array[0..5] of Char = 'Error'#0;
|
|
|
|
|
|
procedure MakeErrorMessage;
|
|
const
|
|
dig : array [0..15] of Char = '0123456789ABCDEF';
|
|
var
|
|
digit: Byte;
|
|
Temp: Integer;
|
|
Addr: Cardinal;
|
|
begin
|
|
digit := 16;
|
|
Temp := ExitCode;
|
|
repeat
|
|
runErrMsg[digit] := Char(Ord('0') + (Temp mod 10));
|
|
Temp := Temp div 10;
|
|
Dec(digit);
|
|
until Temp = 0;
|
|
digit := 28;
|
|
Addr := Cardinal(ErrorAddr);
|
|
repeat
|
|
runErrMsg[digit] := dig[Addr and $F];
|
|
Addr := Addr div 16;
|
|
Dec(digit);
|
|
until Addr = 0;
|
|
end;
|
|
|
|
|
|
procedure ExitDll;
|
|
asm
|
|
{ Return False if ExitCode <> 0, and set ExitCode to 0 }
|
|
|
|
XOR EAX,EAX
|
|
{$IFDEF PIC}
|
|
MOV ECX,[EBX].ExitCode
|
|
XCHG EAX,[ECX]
|
|
{$ELSE}
|
|
XCHG EAX, ExitCode
|
|
{$ENDIF}
|
|
NEG EAX
|
|
SBB EAX,EAX
|
|
INC EAX
|
|
|
|
{ Restore the InitContext }
|
|
{$IFDEF PIC}
|
|
LEA EDI, [EBX].InitContext
|
|
{$ELSE}
|
|
MOV EDI, offset InitContext
|
|
{$ENDIF}
|
|
|
|
MOV EBX,[EDI].TInitContext.DLLSaveEBX
|
|
MOV EBP,[EDI].TInitContext.DLLSaveEBP
|
|
PUSH [EDI].TInitContext.DLLSaveESI
|
|
PUSH [EDI].TInitContext.DLLSaveEDI
|
|
|
|
MOV ESI,[EDI].TInitContext.OuterContext
|
|
MOV ECX,(type TInitContext)/4
|
|
REP MOVSD
|
|
POP EDI
|
|
POP ESI
|
|
|
|
LEAVE
|
|
{$IFDEF MSWINDOWS}
|
|
RET 12
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
RET
|
|
{$ENDIF}
|
|
end;
|
|
|
|
// {X} Procedure Halt0 refers to WriteLn and MessageBox
|
|
// but actually such code can be not used really.
|
|
// So, implementation changed to avoid such references.
|
|
//
|
|
// Either call UseErrorMessageBox or UseErrorMessageWrite
|
|
// to provide error message output in GUI or console app.
|
|
// {X}+
|
|
|
|
var ErrorMessageOutProc : procedure = DummyProc;
|
|
|
|
procedure ErrorMessageBox;
|
|
begin
|
|
MakeErrorMessage;
|
|
if not NoErrMsg then
|
|
MessageBox(0, runErrMsg, errCaption, 0);
|
|
end;
|
|
|
|
procedure UseErrorMessageBox;
|
|
begin
|
|
ErrorMessageOutProc := ErrorMessageBox;
|
|
end;
|
|
|
|
procedure ErrorMessageWrite;
|
|
begin
|
|
MakeErrorMessage;
|
|
WriteLn(PChar(@runErrMsg));
|
|
end;
|
|
|
|
procedure UseErrorMessageWrite;
|
|
begin
|
|
ErrorMessageOutProc := ErrorMessageWrite;
|
|
end;
|
|
|
|
procedure DoCloseInputOutput;
|
|
begin
|
|
Close( Input );
|
|
Close( Output );
|
|
Close(ErrOutput);
|
|
end;
|
|
|
|
var CloseInputOutput : procedure = DummyProc;
|
|
|
|
procedure UseInputOutput;
|
|
begin
|
|
if not assigned( CloseInputOutput ) then
|
|
begin
|
|
CloseInputOutput := DoCloseInputOutput;
|
|
//_Assign( Input, '' ); was for D5 so - changed
|
|
//_Assign( Output, '' ); was for D5 so - changed
|
|
TTextRec(Input).Mode := fmClosed;
|
|
TTextRec(Output).Mode := fmClosed;
|
|
TTextRec(ErrOutput).Mode := fmClosed;
|
|
end;
|
|
end;
|
|
|
|
// {X}-
|
|
(*X-
|
|
procedure WriteErrorMessage;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Dummy: Cardinal;
|
|
begin
|
|
if IsConsole then
|
|
begin
|
|
with TTextRec(Output) do
|
|
begin
|
|
if (Mode = fmOutput) and (BufPos > 0) then
|
|
TTextIOFunc(InOutFunc)(TTextRec(Output)); // flush out text buffer
|
|
end;
|
|
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), runErrMsg, Sizeof(runErrMsg), Dummy, nil);
|
|
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), sLineBreak, 2, Dummy, nil);
|
|
end
|
|
else if not NoErrMsg then
|
|
MessageBox(0, runErrMsg, errCaption, 0);
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
var
|
|
c: Char;
|
|
begin
|
|
with TTextRec(Output) do
|
|
begin
|
|
if (Mode = fmOutput) and (BufPos > 0) then
|
|
TTextIOFunc(InOutFunc)(TTextRec(Output)); // flush out text buffer
|
|
end;
|
|
__write(STDERR_FILENO, @runErrMsg, Sizeof(runErrMsg)-1);
|
|
c := sLineBreak;
|
|
__write(STDERR_FILENO, @c, 1);
|
|
{$ENDIF}
|
|
end;
|
|
X+*)
|
|
|
|
procedure _Halt0;
|
|
var
|
|
P: procedure;
|
|
begin
|
|
{$IFDEF LINUX}
|
|
if (ExitCode <> 0) and CoreDumpEnabled then
|
|
__raise(SIGABRT);
|
|
{$ENDIF}
|
|
|
|
if InitContext.DLLInitState = 0 then
|
|
while ExitProc <> nil do
|
|
begin
|
|
@P := ExitProc;
|
|
ExitProc := nil;
|
|
P;
|
|
end;
|
|
|
|
{ If there was some kind of runtime error, alert the user }
|
|
|
|
if ErrorAddr <> nil then
|
|
begin
|
|
{X+}
|
|
ErrorMessageOutProc;
|
|
{
|
|
MakeErrorMessage;
|
|
if IsConsole then
|
|
WriteLn(PChar(@runErrMsg))
|
|
else if not NoErrMsg then
|
|
MessageBox(0, runErrMsg, errCaption, 0);
|
|
} {X-}
|
|
|
|
{X- As it is said by Alexey Torgashin, it is better not to clear ErrorAddr
|
|
to make possible check ErrorAddr <> nil in finalization of rest units.
|
|
If you want, you can uncomment it again: }
|
|
//ErrorAddr := nil;
|
|
{X+}
|
|
end;
|
|
|
|
{ This loop exists because we might be nested in PackageLoad calls when }
|
|
{ Halt got called. We need to unwind these contexts. }
|
|
|
|
while True do
|
|
begin
|
|
|
|
{ If we are a library, and we are starting up fine, there are no units to finalize }
|
|
|
|
if (InitContext.DLLInitState = 2) and (ExitCode = 0) then
|
|
InitContext.InitCount := 0;
|
|
|
|
{ Undo any unit initializations accomplished so far }
|
|
|
|
// {X} FinalizeUnits; -- renamed
|
|
FInitUnitsProc;
|
|
|
|
if (InitContext.DLLInitState <= 1) or (ExitCode <> 0) then
|
|
begin
|
|
if InitContext.Module <> nil then
|
|
with InitContext do
|
|
begin
|
|
UnregisterModule(Module);
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
SysUnregisterIPLookup(Module.CodeSegStart);
|
|
{$ENDIF}
|
|
if (Module.ResInstance <> Module.Instance) and (Module.ResInstance <> 0) then
|
|
FreeLibrary(Module.ResInstance);
|
|
end;
|
|
end;
|
|
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
{X UnsetExceptionHandler; - changed to call of handler }
|
|
UnsetExceptionHandlerProc;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
if InitContext.DllInitState = 1 then
|
|
InitContext.ExitProcessTLS;
|
|
{$ENDIF}
|
|
|
|
if InitContext.DllInitState <> 0 then
|
|
ExitDll;
|
|
|
|
if InitContext.OuterContext = nil then
|
|
begin
|
|
{
|
|
If an ExitProcessProc is set, we call it. Note that at this
|
|
point the RTL is completely shutdown. The only thing this is used
|
|
for right now is the proper semantic handling of signals under Linux.
|
|
}
|
|
if Assigned(ExitProcessProc) then
|
|
ExitProcessProc;
|
|
ExitProcess(ExitCode);
|
|
end;
|
|
|
|
InitContext := InitContext.OuterContext^
|
|
end;
|
|
end;
|
|
|
|
procedure _Halt;
|
|
begin
|
|
ExitCode := Code;
|
|
_Halt0;
|
|
end;
|
|
|
|
|
|
procedure _Run0Error;
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
_RunError(0); // loses return address
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
XOR EAX,EAX
|
|
JMP _RunError
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
procedure _RunError(errorCode: Byte);
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
ErrorAddr := Pointer(-1); // no return address available
|
|
Halt(errorCode);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
CALL GetGOT
|
|
MOV EBX, EAX
|
|
POP EAX
|
|
MOV ECX, [EBX].ErrorAddr
|
|
POP [ECX]
|
|
{$ELSE}
|
|
POP ErrorAddr
|
|
{$ENDIF}
|
|
JMP _Halt
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _UnhandledException;
|
|
type TExceptProc = procedure (Obj: TObject; Addr: Pointer);
|
|
begin
|
|
if Assigned(ExceptProc) then
|
|
TExceptProc(ExceptProc)(ExceptObject, ExceptAddr)
|
|
else
|
|
RunError(230);
|
|
end;
|
|
|
|
procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
if Assigned(AssertErrorProc) then
|
|
AssertErrorProc(Message, Filename, LineNumber, Pointer(-1))
|
|
else
|
|
Error(reAssertionFailed); // loses return address
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
PUSH EBX
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL GetGOT
|
|
MOV EBX, EAX
|
|
MOV EAX, [EBX].AssertErrorProc
|
|
CMP [EAX], 0
|
|
POP ECX
|
|
POP EAX
|
|
{$ELSE}
|
|
CMP AssertErrorProc,0
|
|
{$ENDIF}
|
|
JNZ @@1
|
|
MOV AL,reAssertionFailed
|
|
CALL Error
|
|
JMP @@exit
|
|
|
|
@@1: PUSH [ESP+4].Pointer
|
|
{$IFDEF PIC}
|
|
MOV EBX, [EBX].AssertErrorProc
|
|
CALL [EBX]
|
|
{$ELSE}
|
|
CALL AssertErrorProc
|
|
{$ENDIF}
|
|
@@exit:
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
type
|
|
PThreadRec = ^TThreadRec;
|
|
TThreadRec = record
|
|
Func: TThreadFunc;
|
|
Parameter: Pointer;
|
|
end;
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
function ThreadWrapper(Parameter: Pointer): Integer; stdcall;
|
|
{$ELSE}
|
|
function ThreadWrapper(Parameter: Pointer): Pointer; cdecl;
|
|
{$ENDIF}
|
|
asm
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
{ Mark the top of the stack with a signature }
|
|
PUSH UNWINDFI_TOPOFSTACK
|
|
{$ENDIF}
|
|
CALL _FpuInit
|
|
PUSH EBP
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
XOR ECX,ECX
|
|
PUSH offset _ExceptionHandler
|
|
MOV EDX,FS:[ECX]
|
|
PUSH EDX
|
|
MOV FS:[ECX],ESP
|
|
{$ENDIF}
|
|
MOV EAX,Parameter
|
|
|
|
MOV ECX,[EAX].TThreadRec.Parameter
|
|
MOV EDX,[EAX].TThreadRec.Func
|
|
PUSH ECX
|
|
PUSH EDX
|
|
CALL _FreeMem
|
|
POP EDX
|
|
POP EAX
|
|
CALL EDX
|
|
|
|
{$IFNDEF PC_MAPPED_EXCEPTIONS}
|
|
XOR EDX,EDX
|
|
POP ECX
|
|
MOV FS:[EDX],ECX
|
|
POP ECX
|
|
{$ENDIF}
|
|
POP EBP
|
|
{$IFDEF PC_MAPPED_EXCEPTIONS}
|
|
{ Ditch our TOS marker }
|
|
ADD ESP, 4
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
|
|
ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
|
|
var ThreadId: LongWord): Integer;
|
|
var
|
|
P: PThreadRec;
|
|
begin
|
|
New(P);
|
|
P.Func := ThreadFunc;
|
|
P.Parameter := Parameter;
|
|
IsMultiThread := TRUE;
|
|
Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P,
|
|
CreationFlags, ThreadID);
|
|
end;
|
|
|
|
|
|
procedure EndThread(ExitCode: Integer);
|
|
begin
|
|
ExitThread(ExitCode);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF LINUX}
|
|
function BeginThread(Attribute: PThreadAttr;
|
|
ThreadFunc: TThreadFunc;
|
|
Parameter: Pointer;
|
|
var ThreadId: Cardinal): Integer;
|
|
var
|
|
P: PThreadRec;
|
|
begin
|
|
if Assigned(BeginThreadProc) then
|
|
Result := BeginThreadProc(Attribute, ThreadFunc, Parameter, ThreadId)
|
|
else
|
|
begin
|
|
New(P);
|
|
P.Func := ThreadFunc;
|
|
P.Parameter := Parameter;
|
|
IsMultiThread := True;
|
|
Result := _pthread_create(ThreadID, Attribute, @ThreadWrapper, P);
|
|
end;
|
|
end;
|
|
|
|
procedure EndThread(ExitCode: Integer);
|
|
begin
|
|
if Assigned(EndThreadProc) then
|
|
EndThreadProc(ExitCode);
|
|
// No "else" required since EndThreadProc does not (!!should not!!) return.
|
|
_pthread_detach(GetCurrentThreadID);
|
|
_pthread_exit(ExitCode);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
type
|
|
PStrRec = ^StrRec;
|
|
StrRec = packed record
|
|
refCnt: Longint;
|
|
length: Longint;
|
|
end;
|
|
|
|
const
|
|
skew = sizeof(StrRec);
|
|
rOff = sizeof(StrRec); { refCnt offset }
|
|
overHead = sizeof(StrRec) + 1;
|
|
|
|
procedure _LStrClr(var S);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
P: PStrRec;
|
|
begin
|
|
if Pointer(S) <> nil then
|
|
begin
|
|
P := Pointer(Integer(S) - Sizeof(StrRec));
|
|
Pointer(S) := nil;
|
|
if P.refCnt > 0 then
|
|
if InterlockedDecrement(P.refCnt) = 0 then
|
|
FreeMem(P);
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to str }
|
|
|
|
MOV EDX,[EAX] { fetch str }
|
|
TEST EDX,EDX { if nil, nothing to do }
|
|
JE @@done
|
|
MOV dword ptr [EAX],0 { clear str }
|
|
MOV ECX,[EDX-skew].StrRec.refCnt { fetch refCnt }
|
|
DEC ECX { if < 0: literal str }
|
|
JL @@done
|
|
{X LOCK} DEC [EDX-skew].StrRec.refCnt { NONthreadsafe dec refCount }
|
|
JNE @@done
|
|
PUSH EAX
|
|
LEA EAX,[EDX-skew].StrRec.refCnt { if refCnt now zero, deallocate}
|
|
CALL _FreeMem
|
|
POP EAX
|
|
@@done:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _LStrArrayClr(var StrArray; cnt: longint);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
P: Pointer;
|
|
begin
|
|
P := @StrArray;
|
|
while cnt > 0 do
|
|
begin
|
|
_LStrClr(P^);
|
|
Dec(cnt);
|
|
Inc(Integer(P), sizeof(Pointer));
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to str }
|
|
{ EDX cnt }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
|
|
@@loop:
|
|
MOV EDX,[EBX] { fetch str }
|
|
TEST EDX,EDX { if nil, nothing to do }
|
|
JE @@doneEntry
|
|
MOV dword ptr [EBX],0 { clear str }
|
|
MOV ECX,[EDX-skew].StrRec.refCnt { fetch refCnt }
|
|
DEC ECX { if < 0: literal str }
|
|
JL @@doneEntry
|
|
{X LOCK} DEC [EDX-skew].StrRec.refCnt { NONthreadsafe dec refCount }
|
|
JNE @@doneEntry
|
|
LEA EAX,[EDX-skew].StrRec.refCnt { if refCnt now zero, deallocate}
|
|
CALL _FreeMem
|
|
@@doneEntry:
|
|
ADD EBX,4
|
|
DEC ESI
|
|
JNE @@loop
|
|
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ 99.03.11
|
|
This function is used when assigning to global variables.
|
|
|
|
Literals are copied to prevent a situation where a dynamically
|
|
allocated DLL or package assigns a literal to a variable and then
|
|
is unloaded -- thereby causing the string memory (in the code
|
|
segment of the DLL) to be removed -- and therefore leaving the
|
|
global variable pointing to invalid memory.
|
|
}
|
|
procedure _LStrAsg(var dest; const source);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
S, D: Pointer;
|
|
P: PStrRec;
|
|
Temp: Longint;
|
|
begin
|
|
S := Pointer(source);
|
|
if S <> nil then
|
|
begin
|
|
P := PStrRec(Integer(S) - sizeof(StrRec));
|
|
if P.refCnt < 0 then // make copy of string literal
|
|
begin
|
|
Temp := P.length;
|
|
S := _NewAnsiString(Temp);
|
|
Move(Pointer(source)^, S^, Temp);
|
|
P := PStrRec(Integer(S) - sizeof(StrRec));
|
|
end;
|
|
InterlockedIncrement(P.refCnt);
|
|
end;
|
|
|
|
D := Pointer(dest);
|
|
Pointer(dest) := S;
|
|
if D <> nil then
|
|
begin
|
|
P := PStrRec(Integer(D) - sizeof(StrRec));
|
|
if P.refCnt > 0 then
|
|
if InterlockedDecrement(P.refCnt) = 0 then
|
|
FreeMem(P);
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to dest str }
|
|
{ -> EDX pointer to source str }
|
|
|
|
TEST EDX,EDX { have a source? }
|
|
JE @@2 { no -> jump }
|
|
|
|
MOV ECX,[EDX-skew].StrRec.refCnt
|
|
INC ECX
|
|
JG @@1 { literal string -> jump not taken }
|
|
|
|
PUSH EAX
|
|
PUSH EDX
|
|
MOV EAX,[EDX-skew].StrRec.length
|
|
CALL _NewAnsiString
|
|
MOV EDX,EAX
|
|
POP EAX
|
|
PUSH EDX
|
|
MOV ECX,[EAX-skew].StrRec.length
|
|
CALL Move
|
|
POP EDX
|
|
POP EAX
|
|
JMP @@2
|
|
|
|
@@1:
|
|
{X LOCK} INC [EDX-skew].StrRec.refCnt
|
|
|
|
@@2: XCHG EDX,[EAX]
|
|
TEST EDX,EDX
|
|
JE @@3
|
|
MOV ECX,[EDX-skew].StrRec.refCnt
|
|
DEC ECX
|
|
JL @@3
|
|
{X LOCK} DEC [EDX-skew].StrRec.refCnt
|
|
JNE @@3
|
|
LEA EAX,[EDX-skew].StrRec.refCnt
|
|
CALL _FreeMem
|
|
@@3:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _LStrLAsg(var dest; const source);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
P: Pointer;
|
|
begin
|
|
P := Pointer(source);
|
|
_LStrAddRef(P);
|
|
P := Pointer(dest);
|
|
Pointer(dest) := Pointer(source);
|
|
_LStrClr(P);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to dest }
|
|
{ EDX source }
|
|
|
|
TEST EDX,EDX
|
|
JE @@sourceDone
|
|
|
|
{ bump up the ref count of the source }
|
|
|
|
MOV ECX,[EDX-skew].StrRec.refCnt
|
|
INC ECX
|
|
JLE @@sourceDone { literal assignment -> jump taken }
|
|
{X LOCK} INC [EDX-skew].StrRec.refCnt
|
|
@@sourceDone:
|
|
|
|
{ we need to release whatever the dest is pointing to }
|
|
|
|
XCHG EDX,[EAX] { fetch str }
|
|
TEST EDX,EDX { if nil, nothing to do }
|
|
JE @@done
|
|
MOV ECX,[EDX-skew].StrRec.refCnt { fetch refCnt }
|
|
DEC ECX { if < 0: literal str }
|
|
JL @@done
|
|
{X LOCK} DEC [EDX-skew].StrRec.refCnt { NONthreadsafe dec refCount }
|
|
JNE @@done
|
|
LEA EAX,[EDX-skew].StrRec.refCnt { if refCnt now zero, deallocate}
|
|
CALL _FreeMem
|
|
@@done:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _NewAnsiString(length: Longint): Pointer;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
P: PStrRec;
|
|
begin
|
|
Result := nil;
|
|
if length <= 0 then Exit;
|
|
// Alloc an extra null for strings with even length. This has no actual cost
|
|
// since the allocator will round up the request to an even size anyway.
|
|
// All widestring allocations have even length, and need a double null terminator.
|
|
GetMem(P, length + sizeof(StrRec) + 1 + ((length + 1) and 1));
|
|
Result := Pointer(Integer(P) + sizeof(StrRec));
|
|
P.length := length;
|
|
P.refcnt := 1;
|
|
PWideChar(Result)[length div 2] := #0; // length guaranteed >= 2
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX length }
|
|
{ <- EAX pointer to new string }
|
|
|
|
TEST EAX,EAX
|
|
JLE @@null
|
|
PUSH EAX
|
|
ADD EAX,rOff+2 // one or two nulls (Ansi/Wide)
|
|
AND EAX, not 1 // round up to even length
|
|
PUSH EAX
|
|
CALL _GetMem
|
|
POP EDX // actual allocated length (>= 2)
|
|
MOV word ptr [EAX+EDX-2],0 // double null terminator
|
|
ADD EAX,rOff
|
|
POP EDX // requested string length
|
|
MOV [EAX-skew].StrRec.length,EDX
|
|
MOV [EAX-skew].StrRec.refCnt,1
|
|
RET
|
|
@@null:
|
|
XOR EAX,EAX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _LStrFromPCharLen(var Dest: AnsiString; Source: PAnsiChar; Length: Integer);
|
|
asm
|
|
{ -> EAX pointer to dest }
|
|
{ EDX source }
|
|
{ ECX length }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
|
|
{ allocate new string }
|
|
|
|
MOV EAX,EDI
|
|
|
|
CALL _NewAnsiString
|
|
MOV ECX,EDI
|
|
MOV EDI,EAX
|
|
|
|
TEST ESI,ESI
|
|
JE @@noMove
|
|
|
|
MOV EDX,EAX
|
|
MOV EAX,ESI
|
|
CALL Move
|
|
|
|
{ assign the result to dest }
|
|
|
|
@@noMove:
|
|
MOV EAX,EBX
|
|
CALL _LStrClr
|
|
MOV [EBX],EDI
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
{$IFDEF LINUX}
|
|
function BufConvert(var Dest; DestBytes: Integer;
|
|
const Source; SrcBytes: Integer;
|
|
context: Integer): Integer;
|
|
var
|
|
SrcBytesLeft, DestBytesLeft: Integer;
|
|
s, d: Pointer;
|
|
begin
|
|
if context = -1 then
|
|
begin
|
|
Result := -1;
|
|
Exit;
|
|
end;
|
|
// make copies of params... iconv modifies param ptrs
|
|
DestBytesLeft := DestBytes;
|
|
SrcBytesLeft := SrcBytes;
|
|
s := Pointer(Source);
|
|
d := Pointer(Dest);
|
|
if (SrcBytes = 0) or (DestBytes = 0) then
|
|
Result := 0
|
|
else
|
|
begin
|
|
Result := iconv(context, s, SrcBytesLeft, d, DestBytesLeft);
|
|
while (SrcBytesLeft > 0) and (DestBytesLeft > 0)
|
|
and (Result = -1) and (GetLastError = 7) do
|
|
begin
|
|
Result := iconv(context, s, SrcBytesLeft, d, DestBytesLeft);
|
|
end;
|
|
|
|
if Result <> -1 then
|
|
Result := DestBytes - DestBytesLeft;
|
|
end;
|
|
|
|
iconv_close(context);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
function CharFromWChar(CharDest: PChar; DestBytes: Integer; const WCharSource: PWideChar; SrcChars: Integer): Integer;
|
|
begin
|
|
{$IFDEF LINUX}
|
|
Result := BufConvert(CharDest, DestBytes, WCharSource, SrcChars * sizeof(WideChar),
|
|
iconv_open(nl_langinfo(_NL_CTYPE_CODESET_NAME), 'UNICODELITTLE'));
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
Result := WideCharToMultiByte(0, 0, WCharSource, SrcChars,
|
|
CharDest, DestBytes, nil, nil);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
function WCharFromChar(WCharDest: PWideChar; DestChars: Integer; const CharSource: PChar; SrcBytes: Integer): Integer;
|
|
begin
|
|
{$IFDEF LINUX}
|
|
Result := BufConvert(WCharDest, DestChars * sizeof(WideChar), CharSource, SrcBytes,
|
|
iconv_open('UNICODELITTLE', nl_langinfo(_NL_CTYPE_CODESET_NAME))) div sizeof(WideChar);
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
Result := MultiByteToWideChar(0, 0, CharSource, SrcBytes,
|
|
WCharDest, DestChars);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
procedure _LStrFromPWCharLen(var Dest: AnsiString; Source: PWideChar; Length: Integer);
|
|
var
|
|
DestLen: Integer;
|
|
Buffer: array[0..4095] of Char;
|
|
begin
|
|
if Length <= 0 then
|
|
begin
|
|
_LStrClr(Dest);
|
|
Exit;
|
|
end;
|
|
if Length+1 < (High(Buffer) div sizeof(WideChar)) then
|
|
begin
|
|
DestLen := CharFromWChar(Buffer, High(Buffer), Source, Length);
|
|
if DestLen >= 0 then
|
|
begin
|
|
_LStrFromPCharLen(Dest, Buffer, DestLen);
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
DestLen := (Length + 1) * sizeof(WideChar);
|
|
SetLength(Dest, DestLen); // overallocate, trim later
|
|
DestLen := CharFromWChar(Pointer(Dest), DestLen, Source, Length);
|
|
if DestLen < 0 then DestLen := 0;
|
|
SetLength(Dest, DestLen);
|
|
end;
|
|
|
|
|
|
procedure _LStrFromChar(var Dest: AnsiString; Source: AnsiChar);
|
|
asm
|
|
PUSH EDX
|
|
MOV EDX,ESP
|
|
MOV ECX,1
|
|
CALL _LStrFromPCharLen
|
|
POP EDX
|
|
end;
|
|
|
|
|
|
procedure _LStrFromWChar(var Dest: AnsiString; Source: WideChar);
|
|
asm
|
|
PUSH EDX
|
|
MOV EDX,ESP
|
|
MOV ECX,1
|
|
CALL _LStrFromPWCharLen
|
|
POP EDX
|
|
end;
|
|
|
|
|
|
procedure _LStrFromPChar(var Dest: AnsiString; Source: PAnsiChar);
|
|
asm
|
|
XOR ECX,ECX
|
|
TEST EDX,EDX
|
|
JE @@5
|
|
PUSH EDX
|
|
@@0: CMP CL,[EDX+0]
|
|
JE @@4
|
|
CMP CL,[EDX+1]
|
|
JE @@3
|
|
CMP CL,[EDX+2]
|
|
JE @@2
|
|
CMP CL,[EDX+3]
|
|
JE @@1
|
|
ADD EDX,4
|
|
JMP @@0
|
|
@@1: INC EDX
|
|
@@2: INC EDX
|
|
@@3: INC EDX
|
|
@@4: MOV ECX,EDX
|
|
POP EDX
|
|
SUB ECX,EDX
|
|
@@5: JMP _LStrFromPCharLen
|
|
end;
|
|
|
|
|
|
procedure _LStrFromPWChar(var Dest: AnsiString; Source: PWideChar);
|
|
asm
|
|
XOR ECX,ECX
|
|
TEST EDX,EDX
|
|
JE @@5
|
|
PUSH EDX
|
|
@@0: CMP CX,[EDX+0]
|
|
JE @@4
|
|
CMP CX,[EDX+2]
|
|
JE @@3
|
|
CMP CX,[EDX+4]
|
|
JE @@2
|
|
CMP CX,[EDX+6]
|
|
JE @@1
|
|
ADD EDX,8
|
|
JMP @@0
|
|
@@1: ADD EDX,2
|
|
@@2: ADD EDX,2
|
|
@@3: ADD EDX,2
|
|
@@4: MOV ECX,EDX
|
|
POP EDX
|
|
SUB ECX,EDX
|
|
SHR ECX,1
|
|
@@5: JMP _LStrFromPWCharLen
|
|
end;
|
|
|
|
|
|
procedure _LStrFromString(var Dest: AnsiString; const Source: ShortString);
|
|
asm
|
|
XOR ECX,ECX
|
|
MOV CL,[EDX]
|
|
INC EDX
|
|
JMP _LStrFromPCharLen
|
|
end;
|
|
|
|
|
|
procedure _LStrFromArray(var Dest: AnsiString; Source: PAnsiChar; Length: Integer);
|
|
asm
|
|
PUSH EDI
|
|
PUSH EAX
|
|
PUSH ECX
|
|
MOV EDI,EDX
|
|
XOR EAX,EAX
|
|
REPNE SCASB
|
|
JNE @@1
|
|
NOT ECX
|
|
@@1: POP EAX
|
|
ADD ECX,EAX
|
|
POP EAX
|
|
POP EDI
|
|
JMP _LStrFromPCharLen
|
|
end;
|
|
|
|
|
|
procedure _LStrFromWArray(var Dest: AnsiString; Source: PWideChar; Length: Integer);
|
|
asm
|
|
PUSH EDI
|
|
PUSH EAX
|
|
PUSH ECX
|
|
MOV EDI,EDX
|
|
XOR EAX,EAX
|
|
REPNE SCASW
|
|
JNE @@1
|
|
NOT ECX
|
|
@@1: POP EAX
|
|
ADD ECX,EAX
|
|
POP EAX
|
|
POP EDI
|
|
JMP _LStrFromPWCharLen
|
|
end;
|
|
|
|
|
|
procedure _LStrFromWStr(var Dest: AnsiString; const Source: WideString);
|
|
asm
|
|
{ -> EAX pointer to dest }
|
|
{ EDX pointer to WideString data }
|
|
|
|
XOR ECX,ECX
|
|
TEST EDX,EDX
|
|
JE @@1
|
|
MOV ECX,[EDX-4]
|
|
SHR ECX,1
|
|
@@1: JMP _LStrFromPWCharLen
|
|
end;
|
|
|
|
|
|
procedure _LStrToString{(var Dest: ShortString; const Source: AnsiString; MaxLen: Integer)};
|
|
asm
|
|
{ -> EAX pointer to result }
|
|
{ EDX AnsiString s }
|
|
{ ECX length of result }
|
|
|
|
PUSH EBX
|
|
TEST EDX,EDX
|
|
JE @@empty
|
|
MOV EBX,[EDX-skew].StrRec.length
|
|
TEST EBX,EBX
|
|
JE @@empty
|
|
|
|
CMP ECX,EBX
|
|
JL @@truncate
|
|
MOV ECX,EBX
|
|
@@truncate:
|
|
MOV [EAX],CL
|
|
INC EAX
|
|
|
|
XCHG EAX,EDX
|
|
CALL Move
|
|
|
|
JMP @@exit
|
|
|
|
@@empty:
|
|
MOV byte ptr [EAX],0
|
|
|
|
@@exit:
|
|
POP EBX
|
|
end;
|
|
|
|
function _LStrLen(const s: AnsiString): Longint;
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
Result := 0;
|
|
if Pointer(s) <> nil then
|
|
Result := PStrRec(Integer(s) - sizeof(StrRec)).length;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX str }
|
|
|
|
TEST EAX,EAX
|
|
JE @@done
|
|
MOV EAX,[EAX-skew].StrRec.length;
|
|
@@done:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
procedure _LStrCat{var dest: AnsiString; source: AnsiString};
|
|
asm
|
|
{ -> EAX pointer to dest }
|
|
{ EDX source }
|
|
|
|
TEST EDX,EDX
|
|
JE @@exit
|
|
|
|
MOV ECX,[EAX]
|
|
TEST ECX,ECX
|
|
JE _LStrAsg
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
MOV EDI,[ECX-skew].StrRec.length
|
|
|
|
MOV EDX,[ESI-skew].StrRec.length
|
|
ADD EDX,EDI
|
|
CMP ESI,ECX
|
|
JE @@appendSelf
|
|
|
|
CALL _LStrSetLength
|
|
MOV EAX,ESI
|
|
MOV ECX,[ESI-skew].StrRec.length
|
|
|
|
@@appendStr:
|
|
MOV EDX,[EBX]
|
|
ADD EDX,EDI
|
|
CALL Move
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
RET
|
|
|
|
@@appendSelf:
|
|
CALL _LStrSetLength
|
|
MOV EAX,[EBX]
|
|
MOV ECX,EDI
|
|
JMP @@appendStr
|
|
|
|
@@exit:
|
|
end;
|
|
|
|
|
|
procedure _LStrCat3{var dest:AnsiString; source1: AnsiString; source2: AnsiString};
|
|
asm
|
|
{ ->EAX = Pointer to dest }
|
|
{ EDX = source1 }
|
|
{ ECX = source2 }
|
|
|
|
TEST EDX,EDX
|
|
JE @@assignSource2
|
|
|
|
TEST ECX,ECX
|
|
JE _LStrAsg
|
|
|
|
CMP EDX,[EAX]
|
|
JE @@appendToDest
|
|
|
|
CMP ECX,[EAX]
|
|
JE @@theHardWay
|
|
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL _LStrAsg
|
|
|
|
POP EDX
|
|
POP EAX
|
|
JMP _LStrCat
|
|
|
|
@@theHardWay:
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV EBX,EDX
|
|
MOV ESI,ECX
|
|
PUSH EAX
|
|
|
|
MOV EAX,[EBX-skew].StrRec.length
|
|
ADD EAX,[ESI-skew].StrRec.length
|
|
CALL _NewAnsiString
|
|
|
|
MOV EDI,EAX
|
|
MOV EDX,EAX
|
|
MOV EAX,EBX
|
|
MOV ECX,[EBX-skew].StrRec.length
|
|
CALL Move
|
|
|
|
MOV EDX,EDI
|
|
MOV EAX,ESI
|
|
MOV ECX,[ESI-skew].StrRec.length
|
|
ADD EDX,[EBX-skew].StrRec.length
|
|
CALL Move
|
|
|
|
POP EAX
|
|
MOV EDX,EDI
|
|
TEST EDI,EDI
|
|
JE @@skip
|
|
DEC [EDI-skew].StrRec.refCnt // EDI = local temp str
|
|
@@skip:
|
|
CALL _LStrAsg
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
|
|
JMP @@exit
|
|
|
|
@@assignSource2:
|
|
MOV EDX,ECX
|
|
JMP _LStrAsg
|
|
|
|
@@appendToDest:
|
|
MOV EDX,ECX
|
|
JMP _LStrCat
|
|
|
|
@@exit:
|
|
end;
|
|
|
|
|
|
procedure _LStrCatN{var dest:AnsiString; argCnt: Integer; ...};
|
|
asm
|
|
{ ->EAX = Pointer to dest }
|
|
{ EDX = number of args (>= 3) }
|
|
{ [ESP+4], [ESP+8], ... crgCnt AnsiString arguments, reverse order }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EDX
|
|
PUSH EAX
|
|
MOV EBX,EDX
|
|
|
|
XOR EDI,EDI
|
|
MOV ECX,[ESP+EDX*4+5*4] // first arg is furthest out
|
|
TEST ECX,ECX
|
|
JZ @@0
|
|
CMP [EAX],ECX // is dest = first arg?
|
|
JNE @@0
|
|
MOV EDI,EAX // EDI nonzero -> potential appendstr case
|
|
@@0:
|
|
XOR EAX,EAX
|
|
@@loop1:
|
|
MOV ECX,[ESP+EDX*4+5*4]
|
|
TEST ECX,ECX
|
|
JE @@1
|
|
ADD EAX,[ECX-skew].StrRec.length
|
|
CMP EDI,ECX // is dest an arg besides arg1?
|
|
JNE @@1
|
|
XOR EDI,EDI // can't appendstr - dest is multiple args
|
|
@@1:
|
|
DEC EDX
|
|
JNE @@loop1
|
|
|
|
@@append:
|
|
TEST EDI,EDI // dest is 1st and only 1st arg?
|
|
JZ @@copy
|
|
MOV EDX,EAX // length into EDX
|
|
MOV EAX,EDI // ptr to str into EAX
|
|
MOV ESI,[EDI]
|
|
MOV ESI,[ESI-skew].StrRec.Length // save old size before realloc
|
|
CALL _LStrSetLength
|
|
PUSH EDI // append other strs to dest
|
|
ADD ESI,[EDI] // end of old string
|
|
DEC EBX
|
|
JMP @@loop2
|
|
|
|
@@copy:
|
|
CALL _NewAnsiString
|
|
PUSH EAX
|
|
MOV ESI,EAX
|
|
|
|
@@loop2:
|
|
MOV EAX,[ESP+EBX*4+6*4]
|
|
MOV EDX,ESI
|
|
TEST EAX,EAX
|
|
JE @@2
|
|
MOV ECX,[EAX-skew].StrRec.length
|
|
ADD ESI,ECX
|
|
CALL Move
|
|
@@2:
|
|
DEC EBX
|
|
JNE @@loop2
|
|
|
|
POP EDX
|
|
POP EAX
|
|
TEST EDI,EDI
|
|
JNZ @@exit
|
|
|
|
TEST EDX,EDX
|
|
JE @@skip
|
|
DEC [EDX-skew].StrRec.refCnt // EDX = local temp str
|
|
@@skip:
|
|
CALL _LStrAsg
|
|
|
|
@@exit:
|
|
POP EDX
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
POP EAX
|
|
LEA ESP,[ESP+EDX*4]
|
|
JMP EAX
|
|
end;
|
|
|
|
|
|
procedure _LStrCmp{left: AnsiString; right: AnsiString};
|
|
asm
|
|
{ ->EAX = Pointer to left string }
|
|
{ EDX = Pointer to right string }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
|
|
CMP EAX,EDX
|
|
JE @@exit
|
|
|
|
TEST ESI,ESI
|
|
JE @@str1null
|
|
|
|
TEST EDI,EDI
|
|
JE @@str2null
|
|
|
|
MOV EAX,[ESI-skew].StrRec.length
|
|
MOV EDX,[EDI-skew].StrRec.length
|
|
|
|
SUB EAX,EDX { eax = len1 - len2 }
|
|
JA @@skip1
|
|
ADD EDX,EAX { edx = len2 + (len1 - len2) = len1 }
|
|
|
|
@@skip1:
|
|
PUSH EDX
|
|
SHR EDX,2
|
|
JE @@cmpRest
|
|
@@longLoop:
|
|
MOV ECX,[ESI]
|
|
MOV EBX,[EDI]
|
|
CMP ECX,EBX
|
|
JNE @@misMatch
|
|
DEC EDX
|
|
JE @@cmpRestP4
|
|
MOV ECX,[ESI+4]
|
|
MOV EBX,[EDI+4]
|
|
CMP ECX,EBX
|
|
JNE @@misMatch
|
|
ADD ESI,8
|
|
ADD EDI,8
|
|
DEC EDX
|
|
JNE @@longLoop
|
|
JMP @@cmpRest
|
|
@@cmpRestP4:
|
|
ADD ESI,4
|
|
ADD EDI,4
|
|
@@cmpRest:
|
|
POP EDX
|
|
AND EDX,3
|
|
JE @@equal
|
|
|
|
MOV ECX,[ESI]
|
|
MOV EBX,[EDI]
|
|
CMP CL,BL
|
|
JNE @@exit
|
|
DEC EDX
|
|
JE @@equal
|
|
CMP CH,BH
|
|
JNE @@exit
|
|
DEC EDX
|
|
JE @@equal
|
|
AND EBX,$00FF0000
|
|
AND ECX,$00FF0000
|
|
CMP ECX,EBX
|
|
JNE @@exit
|
|
|
|
@@equal:
|
|
ADD EAX,EAX
|
|
JMP @@exit
|
|
|
|
@@str1null:
|
|
MOV EDX,[EDI-skew].StrRec.length
|
|
SUB EAX,EDX
|
|
JMP @@exit
|
|
|
|
@@str2null:
|
|
MOV EAX,[ESI-skew].StrRec.length
|
|
SUB EAX,EDX
|
|
JMP @@exit
|
|
|
|
@@misMatch:
|
|
POP EDX
|
|
CMP CL,BL
|
|
JNE @@exit
|
|
CMP CH,BH
|
|
JNE @@exit
|
|
SHR ECX,16
|
|
SHR EBX,16
|
|
CMP CL,BL
|
|
JNE @@exit
|
|
CMP CH,BH
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
|
|
end;
|
|
|
|
function _LStrAddRef(var str): Pointer;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
P: PStrRec;
|
|
begin
|
|
P := Pointer(Integer(str) - sizeof(StrRec));
|
|
if P <> nil then
|
|
if P.refcnt >= 0 then
|
|
InterlockedIncrement(P.refcnt);
|
|
Result := Pointer(str);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX str }
|
|
TEST EAX,EAX
|
|
JE @@exit
|
|
MOV EDX,[EAX-skew].StrRec.refCnt
|
|
INC EDX
|
|
JLE @@exit
|
|
{X LOCK} INC [EAX-skew].StrRec.refCnt
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function PICEmptyString: PWideChar;
|
|
begin
|
|
Result := '';
|
|
end;
|
|
|
|
function _LStrToPChar(const s: AnsiString): PChar;
|
|
{$IFDEF PUREPASCAL}
|
|
const
|
|
EmptyString = '';
|
|
begin
|
|
if Pointer(s) = nil then
|
|
Result := EmptyString
|
|
else
|
|
Result := Pointer(s);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to str }
|
|
{ <- EAX pointer to PChar }
|
|
|
|
TEST EAX,EAX
|
|
JE @@handle0
|
|
RET
|
|
{$IFDEF PIC}
|
|
@@handle0:
|
|
JMP PICEmptyString
|
|
{$ELSE}
|
|
@@zeroByte:
|
|
DB 0
|
|
@@handle0:
|
|
MOV EAX,offset @@zeroByte
|
|
{$ENDIF}
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function InternalUniqueString(var str): Pointer;
|
|
asm
|
|
{ -> EAX pointer to str }
|
|
{ <- EAX pointer to unique copy }
|
|
MOV EDX,[EAX]
|
|
TEST EDX,EDX
|
|
JE @@exit
|
|
MOV ECX,[EDX-skew].StrRec.refCnt
|
|
DEC ECX
|
|
JE @@exit
|
|
|
|
PUSH EBX
|
|
MOV EBX,EAX
|
|
MOV EAX,[EDX-skew].StrRec.length
|
|
CALL _NewAnsiString
|
|
MOV EDX,EAX
|
|
MOV EAX,[EBX]
|
|
MOV [EBX],EDX
|
|
PUSH EAX
|
|
MOV ECX,[EAX-skew].StrRec.length
|
|
CALL Move
|
|
POP EAX
|
|
MOV ECX,[EAX-skew].StrRec.refCnt
|
|
DEC ECX
|
|
JL @@skip
|
|
{X LOCK} DEC [EAX-skew].StrRec.refCnt
|
|
JNZ @@skip
|
|
LEA EAX,[EAX-skew].StrRec.refCnt { if refCnt now zero, deallocate}
|
|
CALL _FreeMem
|
|
@@skip:
|
|
MOV EDX,[EBX]
|
|
POP EBX
|
|
@@exit:
|
|
MOV EAX,EDX
|
|
end;
|
|
|
|
|
|
procedure UniqueString(var str: AnsiString);
|
|
asm
|
|
JMP InternalUniqueString
|
|
end;
|
|
|
|
procedure _UniqueStringA(var str: AnsiString);
|
|
asm
|
|
JMP InternalUniqueString
|
|
end;
|
|
|
|
procedure UniqueString(var str: WideString);
|
|
asm
|
|
{$IFDEF LINUX}
|
|
JMP InternalUniqueString
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
// nothing to do - Windows WideStrings are always single reference
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _UniqueStringW(var str: WideString);
|
|
asm
|
|
{$IFDEF LINUX}
|
|
JMP InternalUniqueString
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
// nothing to do - Windows WideStrings are always single reference
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _LStrCopy{ const s : AnsiString; index, count : Integer) : AnsiString};
|
|
asm
|
|
{ ->EAX Source string }
|
|
{ EDX index }
|
|
{ ECX count }
|
|
{ [ESP+4] Pointer to result string }
|
|
|
|
PUSH EBX
|
|
|
|
TEST EAX,EAX
|
|
JE @@srcEmpty
|
|
|
|
MOV EBX,[EAX-skew].StrRec.length
|
|
TEST EBX,EBX
|
|
JE @@srcEmpty
|
|
|
|
{ make index 0-based and limit to 0 <= index < Length(src) }
|
|
|
|
DEC EDX
|
|
JL @@smallInx
|
|
CMP EDX,EBX
|
|
JGE @@bigInx
|
|
|
|
@@cont1:
|
|
|
|
{ limit count to satisfy 0 <= count <= Length(src) - index }
|
|
|
|
SUB EBX,EDX { calculate Length(src) - index }
|
|
TEST ECX,ECX
|
|
JL @@smallCount
|
|
CMP ECX,EBX
|
|
JG @@bigCount
|
|
|
|
@@cont2:
|
|
|
|
ADD EDX,EAX
|
|
MOV EAX,[ESP+4+4]
|
|
CALL _LStrFromPCharLen
|
|
JMP @@exit
|
|
|
|
@@smallInx:
|
|
XOR EDX,EDX
|
|
JMP @@cont1
|
|
@@bigCount:
|
|
MOV ECX,EBX
|
|
JMP @@cont2
|
|
@@bigInx:
|
|
@@smallCount:
|
|
@@srcEmpty:
|
|
MOV EAX,[ESP+4+4]
|
|
CALL _LStrClr
|
|
@@exit:
|
|
POP EBX
|
|
RET 4
|
|
end;
|
|
|
|
|
|
procedure _LStrDelete{ var s : AnsiString; index, count : Integer };
|
|
asm
|
|
{ ->EAX Pointer to s }
|
|
{ EDX index }
|
|
{ ECX count }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
|
|
CALL UniqueString
|
|
|
|
MOV EDX,[EBX]
|
|
TEST EDX,EDX { source already empty: nothing to do }
|
|
JE @@exit
|
|
|
|
MOV ECX,[EDX-skew].StrRec.length
|
|
|
|
{ make index 0-based, if not in [0 .. Length(s)-1] do nothing }
|
|
|
|
DEC ESI
|
|
JL @@exit
|
|
CMP ESI,ECX
|
|
JGE @@exit
|
|
|
|
{ limit count to [0 .. Length(s) - index] }
|
|
|
|
TEST EDI,EDI
|
|
JLE @@exit
|
|
SUB ECX,ESI { ECX = Length(s) - index }
|
|
CMP EDI,ECX
|
|
JLE @@1
|
|
MOV EDI,ECX
|
|
@@1:
|
|
|
|
{ move length - index - count characters from s+index+count to s+index }
|
|
|
|
SUB ECX,EDI { ECX = Length(s) - index - count }
|
|
ADD EDX,ESI { EDX = s+index }
|
|
LEA EAX,[EDX+EDI] { EAX = s+index+count }
|
|
CALL Move
|
|
|
|
{ set length(s) to length(s) - count }
|
|
|
|
MOV EDX,[EBX]
|
|
MOV EAX,EBX
|
|
MOV EDX,[EDX-skew].StrRec.length
|
|
SUB EDX,EDI
|
|
CALL _LStrSetLength
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
procedure _LStrInsert{ const source : AnsiString; var s : AnsiString; index : Integer };
|
|
asm
|
|
{ -> EAX source string }
|
|
{ EDX pointer to destination string }
|
|
{ ECX index }
|
|
|
|
TEST EAX,EAX
|
|
JE @@nothingToDo
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBP
|
|
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
|
|
{ make index 0-based and limit to 0 <= index <= Length(s) }
|
|
|
|
MOV EDX,[EDX]
|
|
PUSH EDX
|
|
TEST EDX,EDX
|
|
JE @@sIsNull
|
|
MOV EDX,[EDX-skew].StrRec.length
|
|
@@sIsNull:
|
|
DEC EDI
|
|
JGE @@indexNotLow
|
|
XOR EDI,EDI
|
|
@@indexNotLow:
|
|
CMP EDI,EDX
|
|
JLE @@indexNotHigh
|
|
MOV EDI,EDX
|
|
@@indexNotHigh:
|
|
|
|
MOV EBP,[EBX-skew].StrRec.length
|
|
|
|
{ set length of result to length(source) + length(s) }
|
|
|
|
MOV EAX,ESI
|
|
ADD EDX,EBP
|
|
CALL _LStrSetLength
|
|
POP EAX
|
|
|
|
CMP EAX,EBX
|
|
JNE @@notInsertSelf
|
|
MOV EBX,[ESI]
|
|
|
|
@@notInsertSelf:
|
|
|
|
{ move length(s) - length(source) - index chars from s+index to s+index+length(source) }
|
|
|
|
MOV EAX,[ESI] { EAX = s }
|
|
LEA EDX,[EDI+EBP] { EDX = index + length(source) }
|
|
MOV ECX,[EAX-skew].StrRec.length
|
|
SUB ECX,EDX { ECX = length(s) - length(source) - index }
|
|
ADD EDX,EAX { EDX = s + index + length(source) }
|
|
ADD EAX,EDI { EAX = s + index }
|
|
CALL Move
|
|
|
|
{ copy length(source) chars from source to s+index }
|
|
|
|
MOV EAX,EBX
|
|
MOV EDX,[ESI]
|
|
MOV ECX,EBP
|
|
ADD EDX,EDI
|
|
CALL Move
|
|
|
|
@@exit:
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
@@nothingToDo:
|
|
end;
|
|
|
|
|
|
procedure _LStrPos{ const substr : AnsiString; const s : AnsiString ) : Integer};
|
|
asm
|
|
{ ->EAX Pointer to substr }
|
|
{ EDX Pointer to string }
|
|
{ <-EAX Position of substr in s or 0 }
|
|
|
|
TEST EAX,EAX
|
|
JE @@noWork
|
|
|
|
TEST EDX,EDX
|
|
JE @@stringEmpty
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX { Point ESI to substr }
|
|
MOV EDI,EDX { Point EDI to s }
|
|
|
|
MOV ECX,[EDI-skew].StrRec.length { ECX = Length(s) }
|
|
|
|
PUSH EDI { remember s position to calculate index }
|
|
|
|
MOV EDX,[ESI-skew].StrRec.length { EDX = Length(substr) }
|
|
|
|
DEC EDX { EDX = Length(substr) - 1 }
|
|
JS @@fail { < 0 ? return 0 }
|
|
MOV AL,[ESI] { AL = first char of substr }
|
|
INC ESI { Point ESI to 2'nd char of substr }
|
|
|
|
SUB ECX,EDX { #positions in s to look at }
|
|
{ = Length(s) - Length(substr) + 1 }
|
|
JLE @@fail
|
|
@@loop:
|
|
REPNE SCASB
|
|
JNE @@fail
|
|
MOV EBX,ECX { save outer loop counter }
|
|
PUSH ESI { save outer loop substr pointer }
|
|
PUSH EDI { save outer loop s pointer }
|
|
|
|
MOV ECX,EDX
|
|
REPE CMPSB
|
|
POP EDI { restore outer loop s pointer }
|
|
POP ESI { restore outer loop substr pointer }
|
|
JE @@found
|
|
MOV ECX,EBX { restore outer loop counter }
|
|
JMP @@loop
|
|
|
|
@@fail:
|
|
POP EDX { get rid of saved s pointer }
|
|
XOR EAX,EAX
|
|
JMP @@exit
|
|
|
|
@@stringEmpty:
|
|
XOR EAX,EAX
|
|
JMP @@noWork
|
|
|
|
@@found:
|
|
POP EDX { restore pointer to first char of s }
|
|
MOV EAX,EDI { EDI points of char after match }
|
|
SUB EAX,EDX { the difference is the correct index }
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
@@noWork:
|
|
end;
|
|
|
|
|
|
procedure _LStrSetLength{ var str: AnsiString; newLength: Integer};
|
|
asm
|
|
{ -> EAX Pointer to str }
|
|
{ EDX new length }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
XOR EDI,EDI
|
|
|
|
TEST EDX,EDX
|
|
JLE @@setString
|
|
|
|
MOV EAX,[EBX]
|
|
TEST EAX,EAX
|
|
JE @@copyString
|
|
|
|
CMP [EAX-skew].StrRec.refCnt,1
|
|
JNE @@copyString
|
|
|
|
SUB EAX,rOff
|
|
ADD EDX,rOff+1
|
|
PUSH EAX
|
|
MOV EAX,ESP
|
|
CALL _ReallocMem
|
|
POP EAX
|
|
ADD EAX,rOff
|
|
MOV [EBX],EAX
|
|
MOV [EAX-skew].StrRec.length,ESI
|
|
MOV BYTE PTR [EAX+ESI],0
|
|
JMP @@exit
|
|
|
|
@@copyString:
|
|
MOV EAX,EDX
|
|
CALL _NewAnsiString
|
|
MOV EDI,EAX
|
|
|
|
MOV EAX,[EBX]
|
|
TEST EAX,EAX
|
|
JE @@setString
|
|
|
|
MOV EDX,EDI
|
|
MOV ECX,[EAX-skew].StrRec.length
|
|
CMP ECX,ESI
|
|
JL @@moveString
|
|
MOV ECX,ESI
|
|
|
|
@@moveString:
|
|
CALL Move
|
|
|
|
@@setString:
|
|
MOV EAX,EBX
|
|
CALL _LStrClr
|
|
MOV [EBX],EDI
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
procedure _LStrOfChar{ c: Char; count: Integer): AnsiString };
|
|
asm
|
|
{ -> AL c }
|
|
{ EDX count }
|
|
{ ECX result }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
|
|
MOV EAX,ECX
|
|
CALL _LStrClr
|
|
|
|
TEST ESI,ESI
|
|
JLE @@exit
|
|
|
|
MOV EAX,ESI
|
|
CALL _NewAnsiString
|
|
|
|
MOV [EDI],EAX
|
|
|
|
MOV EDX,ESI
|
|
MOV CL,BL
|
|
|
|
CALL _FillChar
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
|
|
end;
|
|
|
|
|
|
function _Write0LString(var t: TTextRec; const s: AnsiString): Pointer;
|
|
begin
|
|
Result := _WriteLString(t, s, 0);
|
|
end;
|
|
|
|
|
|
function _WriteLString(var t: TTextRec; const s: AnsiString; width: Longint): Pointer;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := Length(s);
|
|
_WriteSpaces(t, width - i);
|
|
Result := _WriteBytes(t, s[1], i);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX Pointer to text record }
|
|
{ EDX Pointer to AnsiString }
|
|
{ ECX Field width }
|
|
|
|
PUSH EBX
|
|
|
|
MOV EBX,EDX
|
|
|
|
MOV EDX,ECX
|
|
XOR ECX,ECX
|
|
TEST EBX,EBX
|
|
JE @@skip
|
|
MOV ECX,[EBX-skew].StrRec.length
|
|
SUB EDX,ECX
|
|
@@skip:
|
|
PUSH ECX
|
|
CALL _WriteSpaces
|
|
POP ECX
|
|
|
|
MOV EDX,EBX
|
|
POP EBX
|
|
JMP _WriteBytes
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
function _Write0WString(var t: TTextRec; const s: WideString): Pointer;
|
|
begin
|
|
Result := _WriteWString(t, s, 0);
|
|
end;
|
|
|
|
|
|
function _WriteWString(var t: TTextRec; const s: WideString; width: Longint): Pointer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := Length(s);
|
|
_WriteSpaces(t, width - i);
|
|
Result := _WriteLString(t, AnsiString(s), 0);
|
|
end;
|
|
|
|
|
|
function _Write0WCString(var t: TTextRec; s: PWideChar): Pointer;
|
|
begin
|
|
Result := _WriteWCString(t, s, 0);
|
|
end;
|
|
|
|
|
|
function _WriteWCString(var t: TTextRec; s: PWideChar; width: Longint): Pointer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := 0;
|
|
if (s <> nil) then
|
|
while s[i] <> #0 do
|
|
Inc(i);
|
|
|
|
_WriteSpaces(t, width - i);
|
|
Result := _WriteLString(t, AnsiString(s), 0);
|
|
end;
|
|
|
|
|
|
function _Write0WChar(var t: TTextRec; c: WideChar): Pointer;
|
|
begin
|
|
Result := _WriteWChar(t, c, 0);
|
|
end;
|
|
|
|
|
|
function _WriteWChar(var t: TTextRec; c: WideChar; width: Integer): Pointer;
|
|
begin
|
|
_WriteSpaces(t, width - 1);
|
|
Result := _WriteLString(t, AnsiString(c), 0);
|
|
end;
|
|
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
procedure WStrError;
|
|
asm
|
|
MOV AL,reOutOfMemory
|
|
JMP Error
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function _NewWideString(CharLength: Longint): Pointer;
|
|
{$IFDEF LINUX}
|
|
begin
|
|
Result := _NewAnsiString(CharLength*2);
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
TEST EAX,EAX
|
|
JE @@1
|
|
PUSH EAX
|
|
PUSH 0
|
|
CALL SysAllocStringLen
|
|
TEST EAX,EAX
|
|
JE WStrError
|
|
@@1:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure WStrSet(var S: WideString; P: PWideChar);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
Temp: Pointer;
|
|
begin
|
|
Temp := Pointer(InterlockedExchange(Pointer(S), Pointer(P)));
|
|
if Temp <> nil then
|
|
_WStrClr(Temp);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{$IFDEF LINUX}
|
|
XCHG [EAX],EDX
|
|
TEST EDX,EDX
|
|
JZ @@1
|
|
PUSH EDX
|
|
MOV EAX, ESP
|
|
CALL _WStrClr
|
|
POP EAX
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
XCHG [EAX],EDX
|
|
TEST EDX,EDX
|
|
JZ @@1
|
|
PUSH EDX
|
|
CALL SysFreeString
|
|
{$ENDIF}
|
|
@@1:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
procedure WStrClr;
|
|
asm
|
|
JMP _WStrClr
|
|
end;
|
|
|
|
procedure _WStrClr(var S);
|
|
{$IFDEF LINUX}
|
|
asm
|
|
JMP _LStrClr;
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
{ -> EAX Pointer to WideString }
|
|
|
|
MOV EDX,[EAX]
|
|
TEST EDX,EDX
|
|
JE @@1
|
|
MOV DWORD PTR [EAX],0
|
|
PUSH EAX
|
|
PUSH EDX
|
|
CALL SysFreeString
|
|
POP EAX
|
|
@@1:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
procedure WStrArrayClr;
|
|
asm
|
|
JMP _WStrArrayClr;
|
|
end;
|
|
|
|
procedure _WStrArrayClr(var StrArray; Count: Integer);
|
|
{$IFDEF LINUX}
|
|
asm
|
|
JMP _LStrArrayClr
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
PUSH EBX
|
|
PUSH ESI
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
@@1: MOV EAX,[EBX]
|
|
TEST EAX,EAX
|
|
JE @@2
|
|
MOV DWORD PTR [EBX],0
|
|
PUSH EAX
|
|
CALL SysFreeString
|
|
@@2: ADD EBX,4
|
|
DEC ESI
|
|
JNE @@1
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
procedure _WStrAsg(var Dest: WideString; const Source: WideString);
|
|
{$IFDEF LINUX}
|
|
asm
|
|
JMP _LStrAsg
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
{ -> EAX Pointer to WideString }
|
|
{ EDX Pointer to data }
|
|
TEST EDX,EDX
|
|
JE _WStrClr
|
|
MOV ECX,[EDX-4]
|
|
SHR ECX,1
|
|
JE _WStrClr
|
|
PUSH ECX
|
|
PUSH EDX
|
|
PUSH EAX
|
|
CALL SysReAllocStringLen
|
|
TEST EAX,EAX
|
|
JE WStrError
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _WStrLAsg(var Dest: WideString; const Source: WideString);
|
|
{$IFDEF LINUX}
|
|
asm
|
|
JMP _LStrLAsg
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
JMP _WStrAsg
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _WStrFromPCharLen(var Dest: WideString; Source: PAnsiChar; Length: Integer);
|
|
var
|
|
DestLen: Integer;
|
|
Buffer: array[0..2047] of WideChar;
|
|
begin
|
|
if Length <= 0 then
|
|
begin
|
|
_WStrClr(Dest);
|
|
Exit;
|
|
end;
|
|
if Length+1 < High(Buffer) then
|
|
begin
|
|
DestLen := WCharFromChar(Buffer, High(Buffer), Source, Length);
|
|
if DestLen > 0 then
|
|
begin
|
|
_WStrFromPWCharLen(Dest, @Buffer, DestLen);
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
DestLen := (Length + 1);
|
|
_WStrSetLength(Dest, DestLen); // overallocate, trim later
|
|
DestLen := WCharFromChar(Pointer(Dest), DestLen, Source, Length);
|
|
if DestLen < 0 then DestLen := 0;
|
|
_WStrSetLength(Dest, DestLen);
|
|
end;
|
|
|
|
procedure _WStrFromPWCharLen(var Dest: WideString; Source: PWideChar; CharLength: Integer);
|
|
{$IFDEF LINUX}
|
|
var
|
|
Temp: Pointer;
|
|
begin
|
|
Temp := Pointer(Dest);
|
|
if CharLength > 0 then
|
|
begin
|
|
Pointer(Dest) := _NewWideString(CharLength);
|
|
if Source <> nil then
|
|
Move(Source^, Pointer(Dest)^, CharLength * sizeof(WideChar));
|
|
end
|
|
else
|
|
Pointer(Dest) := nil;
|
|
_WStrClr(Temp);
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
{ -> EAX Pointer to WideString (dest) }
|
|
{ EDX Pointer to characters (source) }
|
|
{ ECX number of characters (not bytes) }
|
|
TEST ECX,ECX
|
|
JE _WStrClr
|
|
|
|
PUSH EAX
|
|
|
|
PUSH ECX
|
|
PUSH EDX
|
|
CALL SysAllocStringLen
|
|
TEST EAX,EAX
|
|
JE WStrError
|
|
|
|
POP EDX
|
|
PUSH [EDX].PWideChar
|
|
MOV [EDX],EAX
|
|
|
|
CALL SysFreeString
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
procedure _WStrFromChar(var Dest: WideString; Source: AnsiChar);
|
|
asm
|
|
PUSH EDX
|
|
MOV EDX,ESP
|
|
MOV ECX,1
|
|
CALL _WStrFromPCharLen
|
|
POP EDX
|
|
end;
|
|
|
|
|
|
procedure _WStrFromWChar(var Dest: WideString; Source: WideChar);
|
|
asm
|
|
{ -> EAX Pointer to WideString (dest) }
|
|
{ EDX character (source) }
|
|
PUSH EDX
|
|
MOV EDX,ESP
|
|
MOV ECX,1
|
|
CALL _WStrFromPWCharLen
|
|
POP EDX
|
|
end;
|
|
|
|
|
|
procedure _WStrFromPChar(var Dest: WideString; Source: PAnsiChar);
|
|
asm
|
|
{ -> EAX Pointer to WideString (dest) }
|
|
{ EDX Pointer to character (source) }
|
|
XOR ECX,ECX
|
|
TEST EDX,EDX
|
|
JE @@5
|
|
PUSH EDX
|
|
@@0: CMP CL,[EDX+0]
|
|
JE @@4
|
|
CMP CL,[EDX+1]
|
|
JE @@3
|
|
CMP CL,[EDX+2]
|
|
JE @@2
|
|
CMP CL,[EDX+3]
|
|
JE @@1
|
|
ADD EDX,4
|
|
JMP @@0
|
|
@@1: INC EDX
|
|
@@2: INC EDX
|
|
@@3: INC EDX
|
|
@@4: MOV ECX,EDX
|
|
POP EDX
|
|
SUB ECX,EDX
|
|
@@5: JMP _WStrFromPCharLen
|
|
end;
|
|
|
|
|
|
procedure _WStrFromPWChar(var Dest: WideString; Source: PWideChar);
|
|
asm
|
|
{ -> EAX Pointer to WideString (dest) }
|
|
{ EDX Pointer to character (source) }
|
|
XOR ECX,ECX
|
|
TEST EDX,EDX
|
|
JE @@5
|
|
PUSH EDX
|
|
@@0: CMP CX,[EDX+0]
|
|
JE @@4
|
|
CMP CX,[EDX+2]
|
|
JE @@3
|
|
CMP CX,[EDX+4]
|
|
JE @@2
|
|
CMP CX,[EDX+6]
|
|
JE @@1
|
|
ADD EDX,8
|
|
JMP @@0
|
|
@@1: ADD EDX,2
|
|
@@2: ADD EDX,2
|
|
@@3: ADD EDX,2
|
|
@@4: MOV ECX,EDX
|
|
POP EDX
|
|
SUB ECX,EDX
|
|
SHR ECX,1
|
|
@@5: JMP _WStrFromPWCharLen
|
|
end;
|
|
|
|
|
|
procedure _WStrFromString(var Dest: WideString; const Source: ShortString);
|
|
asm
|
|
XOR ECX,ECX
|
|
MOV CL,[EDX]
|
|
INC EDX
|
|
JMP _WStrFromPCharLen
|
|
end;
|
|
|
|
|
|
procedure _WStrFromArray(var Dest: WideString; Source: PAnsiChar; Length: Integer);
|
|
asm
|
|
PUSH EDI
|
|
PUSH EAX
|
|
PUSH ECX
|
|
MOV EDI,EDX
|
|
XOR EAX,EAX
|
|
REPNE SCASB
|
|
JNE @@1
|
|
NOT ECX
|
|
@@1: POP EAX
|
|
ADD ECX,EAX
|
|
POP EAX
|
|
POP EDI
|
|
JMP _WStrFromPCharLen
|
|
end;
|
|
|
|
|
|
procedure _WStrFromWArray(var Dest: WideString; Source: PWideChar; Length: Integer);
|
|
asm
|
|
PUSH EDI
|
|
PUSH EAX
|
|
PUSH ECX
|
|
MOV EDI,EDX
|
|
XOR EAX,EAX
|
|
REPNE SCASW
|
|
JNE @@1
|
|
NOT ECX
|
|
@@1: POP EAX
|
|
ADD ECX,EAX
|
|
POP EAX
|
|
POP EDI
|
|
JMP _WStrFromPWCharLen
|
|
end;
|
|
|
|
|
|
procedure _WStrFromLStr(var Dest: WideString; const Source: AnsiString);
|
|
asm
|
|
XOR ECX,ECX
|
|
TEST EDX,EDX
|
|
JE @@1
|
|
MOV ECX,[EDX-4]
|
|
@@1: JMP _WStrFromPCharLen
|
|
end;
|
|
|
|
|
|
procedure _WStrToString(Dest: PShortString; const Source: WideString; MaxLen: Integer);
|
|
var
|
|
SourceLen, DestLen: Integer;
|
|
Buffer: array[0..511] of Char;
|
|
begin
|
|
if MaxLen > 255 then MaxLen := 255;
|
|
SourceLen := Length(Source);
|
|
if SourceLen >= MaxLen then SourceLen := MaxLen;
|
|
if SourceLen = 0 then
|
|
DestLen := 0
|
|
else
|
|
begin
|
|
DestLen := CharFromWChar(Buffer, High(Buffer), PWideChar(Pointer(Source)), SourceLen);
|
|
if DestLen < 0 then
|
|
DestLen := 0
|
|
else if DestLen > MaxLen then
|
|
DestLen := MaxLen;
|
|
end;
|
|
Dest^[0] := Chr(DestLen);
|
|
if DestLen > 0 then Move(Buffer, Dest^[1], DestLen);
|
|
end;
|
|
|
|
function _WStrToPWChar(const S: WideString): PWideChar;
|
|
{$IFDEF PUREPASCAL}
|
|
const
|
|
EmptyString = '';
|
|
begin
|
|
if Pointer(S) = nil then
|
|
Result := EmptyString
|
|
else
|
|
Result := Pointer(S);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
TEST EAX,EAX
|
|
JE @@1
|
|
RET
|
|
{$IFDEF PIC}
|
|
@@1: JMP PICEmptyString
|
|
{$ELSE}
|
|
NOP
|
|
@@0: DW 0
|
|
@@1: MOV EAX,OFFSET @@0
|
|
{$ENDIF}
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
function _WStrLen(const S: WideString): Integer;
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
if Pointer(S) = nil then
|
|
Result := 0
|
|
else
|
|
Result := PInteger(Integer(S) - 4)^ div sizeof(WideChar);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX Pointer to WideString data }
|
|
TEST EAX,EAX
|
|
JE @@1
|
|
MOV EAX,[EAX-4]
|
|
SHR EAX,1
|
|
@@1:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _WStrCat(var Dest: WideString; const Source: WideString);
|
|
var
|
|
DestLen, SourceLen: Integer;
|
|
NewStr: PWideChar;
|
|
begin
|
|
SourceLen := Length(Source);
|
|
if SourceLen <> 0 then
|
|
begin
|
|
DestLen := Length(Dest);
|
|
NewStr := _NewWideString(DestLen + SourceLen);
|
|
if DestLen > 0 then
|
|
Move(Pointer(Dest)^, NewStr^, DestLen * sizeof(WideChar));
|
|
Move(Pointer(Source)^, NewStr[DestLen], SourceLen * sizeof(WideChar));
|
|
WStrSet(Dest, NewStr);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure _WStrCat3(var Dest: WideString; const Source1, Source2: WideString);
|
|
var
|
|
Source1Len, Source2Len: Integer;
|
|
NewStr: PWideChar;
|
|
begin
|
|
Source1Len := Length(Source1);
|
|
Source2Len := Length(Source2);
|
|
if (Source1Len <> 0) or (Source2Len <> 0) then
|
|
begin
|
|
NewStr := _NewWideString(Source1Len + Source2Len);
|
|
Move(Pointer(Source1)^, Pointer(NewStr)^, Source1Len * sizeof(WideChar));
|
|
Move(Pointer(Source2)^, NewStr[Source1Len], Source2Len * sizeof(WideChar));
|
|
WStrSet(Dest, NewStr);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure _WStrCatN{var Dest: WideString; ArgCnt: Integer; ...};
|
|
asm
|
|
{ ->EAX = Pointer to dest }
|
|
{ EDX = number of args (>= 3) }
|
|
{ [ESP+4], [ESP+8], ... crgCnt WideString arguments }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDX
|
|
PUSH EAX
|
|
MOV EBX,EDX
|
|
|
|
XOR EAX,EAX
|
|
@@loop1:
|
|
MOV ECX,[ESP+EDX*4+4*4]
|
|
TEST ECX,ECX
|
|
JE @@1
|
|
ADD EAX,[ECX-4]
|
|
@@1:
|
|
DEC EDX
|
|
JNE @@loop1
|
|
|
|
SHR EAX,1
|
|
CALL _NewWideString
|
|
PUSH EAX
|
|
MOV ESI,EAX
|
|
|
|
@@loop2:
|
|
MOV EAX,[ESP+EBX*4+5*4]
|
|
MOV EDX,ESI
|
|
TEST EAX,EAX
|
|
JE @@2
|
|
MOV ECX,[EAX-4]
|
|
ADD ESI,ECX
|
|
CALL Move
|
|
@@2:
|
|
DEC EBX
|
|
JNE @@loop2
|
|
|
|
POP EDX
|
|
POP EAX
|
|
CALL WStrSet
|
|
|
|
POP EDX
|
|
POP ESI
|
|
POP EBX
|
|
POP EAX
|
|
LEA ESP,[ESP+EDX*4]
|
|
JMP EAX
|
|
end;
|
|
|
|
|
|
procedure _WStrCmp{left: WideString; right: WideString};
|
|
asm
|
|
{ ->EAX = Pointer to left string }
|
|
{ EDX = Pointer to right string }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
|
|
CMP EAX,EDX
|
|
JE @@exit
|
|
|
|
TEST ESI,ESI
|
|
JE @@str1null
|
|
|
|
TEST EDI,EDI
|
|
JE @@str2null
|
|
|
|
MOV EAX,[ESI-4]
|
|
MOV EDX,[EDI-4]
|
|
|
|
SUB EAX,EDX { eax = len1 - len2 }
|
|
JA @@skip1
|
|
ADD EDX,EAX { edx = len2 + (len1 - len2) = len1 }
|
|
|
|
@@skip1:
|
|
PUSH EDX
|
|
SHR EDX,2
|
|
JE @@cmpRest
|
|
@@longLoop:
|
|
MOV ECX,[ESI]
|
|
MOV EBX,[EDI]
|
|
CMP ECX,EBX
|
|
JNE @@misMatch
|
|
DEC EDX
|
|
JE @@cmpRestP4
|
|
MOV ECX,[ESI+4]
|
|
MOV EBX,[EDI+4]
|
|
CMP ECX,EBX
|
|
JNE @@misMatch
|
|
ADD ESI,8
|
|
ADD EDI,8
|
|
DEC EDX
|
|
JNE @@longLoop
|
|
JMP @@cmpRest
|
|
@@cmpRestP4:
|
|
ADD ESI,4
|
|
ADD EDI,4
|
|
@@cmpRest:
|
|
POP EDX
|
|
AND EDX,2
|
|
JE @@equal
|
|
|
|
MOV CX,[ESI]
|
|
MOV BX,[EDI]
|
|
CMP CX,BX
|
|
JNE @@exit
|
|
|
|
@@equal:
|
|
ADD EAX,EAX
|
|
JMP @@exit
|
|
|
|
@@str1null:
|
|
MOV EDX,[EDI-4]
|
|
SUB EAX,EDX
|
|
JMP @@exit
|
|
|
|
@@str2null:
|
|
MOV EAX,[ESI-4]
|
|
SUB EAX,EDX
|
|
JMP @@exit
|
|
|
|
@@misMatch:
|
|
POP EDX
|
|
CMP CX,BX
|
|
JNE @@exit
|
|
SHR ECX,16
|
|
SHR EBX,16
|
|
CMP CX,BX
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
function _WStrCopy(const S: WideString; Index, Count: Integer): WideString;
|
|
var
|
|
L, N: Integer;
|
|
begin
|
|
L := Length(S);
|
|
if Index < 1 then Index := 0 else
|
|
begin
|
|
Dec(Index);
|
|
if Index > L then Index := L;
|
|
end;
|
|
if Count < 0 then N := 0 else
|
|
begin
|
|
N := L - Index;
|
|
if N > Count then N := Count;
|
|
end;
|
|
_WStrFromPWCharLen(Result, PWideChar(Pointer(S)) + Index, N);
|
|
end;
|
|
|
|
|
|
procedure _WStrDelete(var S: WideString; Index, Count: Integer);
|
|
var
|
|
L, N: Integer;
|
|
NewStr: PWideChar;
|
|
begin
|
|
L := Length(S);
|
|
if (L > 0) and (Index >= 1) and (Index <= L) and (Count > 0) then
|
|
begin
|
|
Dec(Index);
|
|
N := L - Index - Count;
|
|
if N < 0 then N := 0;
|
|
if (Index = 0) and (N = 0) then NewStr := nil else
|
|
begin
|
|
NewStr := _NewWideString(Index + N);
|
|
if Index > 0 then
|
|
Move(Pointer(S)^, NewStr^, Index * 2);
|
|
if N > 0 then
|
|
Move(PWideChar(Pointer(S))[L - N], NewStr[Index], N * 2);
|
|
end;
|
|
WStrSet(S, NewStr);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure _WStrInsert(const Source: WideString; var Dest: WideString; Index: Integer);
|
|
var
|
|
SourceLen, DestLen: Integer;
|
|
NewStr: PWideChar;
|
|
begin
|
|
SourceLen := Length(Source);
|
|
if SourceLen > 0 then
|
|
begin
|
|
DestLen := Length(Dest);
|
|
if Index < 1 then Index := 0 else
|
|
begin
|
|
Dec(Index);
|
|
if Index > DestLen then Index := DestLen;
|
|
end;
|
|
NewStr := _NewWideString(DestLen + SourceLen);
|
|
if Index > 0 then
|
|
Move(Pointer(Dest)^, NewStr^, Index * 2);
|
|
Move(Pointer(Source)^, NewStr[Index], SourceLen * 2);
|
|
if Index < DestLen then
|
|
Move(PWideChar(Pointer(Dest))[Index], NewStr[Index + SourceLen],
|
|
(DestLen - Index) * 2);
|
|
WStrSet(Dest, NewStr);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure _WStrPos{ const substr : WideString; const s : WideString ) : Integer};
|
|
asm
|
|
{ ->EAX Pointer to substr }
|
|
{ EDX Pointer to string }
|
|
{ <-EAX Position of substr in s or 0 }
|
|
|
|
TEST EAX,EAX
|
|
JE @@noWork
|
|
|
|
TEST EDX,EDX
|
|
JE @@stringEmpty
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV ESI,EAX { Point ESI to substr }
|
|
MOV EDI,EDX { Point EDI to s }
|
|
|
|
MOV ECX,[EDI-4] { ECX = Length(s) }
|
|
SHR ECX,1
|
|
|
|
PUSH EDI { remember s position to calculate index }
|
|
|
|
MOV EDX,[ESI-4] { EDX = Length(substr) }
|
|
SHR EDX,1
|
|
|
|
DEC EDX { EDX = Length(substr) - 1 }
|
|
JS @@fail { < 0 ? return 0 }
|
|
MOV AX,[ESI] { AL = first char of substr }
|
|
ADD ESI,2 { Point ESI to 2'nd char of substr }
|
|
|
|
SUB ECX,EDX { #positions in s to look at }
|
|
{ = Length(s) - Length(substr) + 1 }
|
|
JLE @@fail
|
|
@@loop:
|
|
REPNE SCASW
|
|
JNE @@fail
|
|
MOV EBX,ECX { save outer loop counter }
|
|
PUSH ESI { save outer loop substr pointer }
|
|
PUSH EDI { save outer loop s pointer }
|
|
|
|
MOV ECX,EDX
|
|
REPE CMPSW
|
|
POP EDI { restore outer loop s pointer }
|
|
POP ESI { restore outer loop substr pointer }
|
|
JE @@found
|
|
MOV ECX,EBX { restore outer loop counter }
|
|
JMP @@loop
|
|
|
|
@@fail:
|
|
POP EDX { get rid of saved s pointer }
|
|
XOR EAX,EAX
|
|
JMP @@exit
|
|
|
|
@@stringEmpty:
|
|
XOR EAX,EAX
|
|
JMP @@noWork
|
|
|
|
@@found:
|
|
POP EDX { restore pointer to first char of s }
|
|
MOV EAX,EDI { EDI points of char after match }
|
|
SUB EAX,EDX { the difference is the correct index }
|
|
SHR EAX,1
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
@@noWork:
|
|
end;
|
|
|
|
|
|
procedure _WStrSetLength(var S: WideString; NewLength: Integer);
|
|
var
|
|
NewStr: PWideChar;
|
|
Count: Integer;
|
|
begin
|
|
NewStr := nil;
|
|
if NewLength > 0 then
|
|
begin
|
|
NewStr := _NewWideString(NewLength);
|
|
Count := Length(S);
|
|
if Count > 0 then
|
|
begin
|
|
if Count > NewLength then Count := NewLength;
|
|
Move(Pointer(S)^, NewStr^, Count * 2);
|
|
end;
|
|
end;
|
|
WStrSet(S, NewStr);
|
|
end;
|
|
|
|
|
|
function _WStrOfWChar(Ch: WideChar; Count: Integer): WideString;
|
|
var
|
|
P: PWideChar;
|
|
begin
|
|
_WStrFromPWCharLen(Result, nil, Count);
|
|
P := Pointer(Result);
|
|
while Count > 0 do
|
|
begin
|
|
Dec(Count);
|
|
P[Count] := Ch;
|
|
end;
|
|
end;
|
|
|
|
procedure WStrAddRef;
|
|
asm
|
|
JMP _WStrAddRef
|
|
end;
|
|
|
|
function _WStrAddRef(var str: WideString): Pointer;
|
|
{$IFDEF LINUX}
|
|
asm
|
|
JMP _LStrAddRef
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
asm
|
|
MOV EDX,[EAX]
|
|
TEST EDX,EDX
|
|
JE @@1
|
|
PUSH EAX
|
|
MOV ECX,[EDX-4]
|
|
SHR ECX,1
|
|
PUSH ECX
|
|
PUSH EDX
|
|
CALL SysAllocStringLen
|
|
POP EDX
|
|
TEST EAX,EAX
|
|
JE WStrError
|
|
MOV [EDX],EAX
|
|
@@1:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
type
|
|
PPTypeInfo = ^PTypeInfo;
|
|
PTypeInfo = ^TTypeInfo;
|
|
TTypeInfo = packed record
|
|
Kind: Byte;
|
|
Name: ShortString;
|
|
{TypeData: TTypeData}
|
|
end;
|
|
|
|
TFieldInfo = packed record
|
|
TypeInfo: PPTypeInfo;
|
|
Offset: Cardinal;
|
|
end;
|
|
|
|
PFieldTable = ^TFieldTable;
|
|
TFieldTable = packed record
|
|
X: Word;
|
|
Size: Cardinal;
|
|
Count: Cardinal;
|
|
Fields: array [0..0] of TFieldInfo;
|
|
end;
|
|
|
|
{ ===========================================================================
|
|
InitializeRecord, InitializeArray, and Initialize are PIC safe even though
|
|
they alter EBX because they only call each other. They never call out to
|
|
other functions and they don't access global data.
|
|
|
|
FinalizeRecord, Finalize, and FinalizeArray are PIC safe because they call
|
|
Pascal routines which will have EBX fixup prologs.
|
|
===========================================================================}
|
|
|
|
procedure _InitializeRecord(p: Pointer; typeInfo: Pointer);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
FT: PFieldTable;
|
|
I: Cardinal;
|
|
begin
|
|
FT := PFieldTable(Integer(typeInfo) + Byte(PTypeInfo(typeInfo).Name[0]));
|
|
for I := FT.Count-1 downto 0 do
|
|
_InitializeArray(Pointer(Cardinal(P) + FT.Fields[I].Offset), FT.Fields[I].TypeInfo^, 1);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to record to be initialized }
|
|
{ EDX pointer to type info }
|
|
|
|
XOR ECX,ECX
|
|
|
|
PUSH EBX
|
|
MOV CL,[EDX+1] { type name length }
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV EBX,EAX // PIC safe. See comment above
|
|
LEA ESI,[EDX+ECX+2+8] { address of destructable fields }
|
|
MOV EDI,[EDX+ECX+2+4] { number of destructable fields }
|
|
|
|
@@loop:
|
|
|
|
MOV EDX,[ESI]
|
|
MOV EAX,[ESI+4]
|
|
ADD EAX,EBX
|
|
MOV EDX,[EDX]
|
|
MOV ECX,1
|
|
CALL _InitializeArray
|
|
ADD ESI,8
|
|
DEC EDI
|
|
JG @@loop
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
const
|
|
tkLString = 10;
|
|
tkWString = 11;
|
|
tkVariant = 12;
|
|
tkArray = 13;
|
|
tkRecord = 14;
|
|
tkInterface = 15;
|
|
tkDynArray = 17;
|
|
|
|
procedure _InitializeArray(p: Pointer; typeInfo: Pointer; elemCount: Cardinal);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
FT: PFieldTable;
|
|
begin
|
|
if elemCount = 0 then Exit;
|
|
case PTypeInfo(typeInfo).Kind of
|
|
tkLString, tkWString, tkInterface, tkDynArray:
|
|
while elemCount > 0 do
|
|
begin
|
|
PInteger(P)^ := 0;
|
|
Inc(Integer(P), 4);
|
|
Dec(elemCount);
|
|
end;
|
|
tkVariant:
|
|
while elemCount > 0 do
|
|
begin
|
|
PInteger(P)^ := 0;
|
|
PInteger(Integer(P)+4)^ := 0;
|
|
PInteger(Integer(P)+8)^ := 0;
|
|
PInteger(Integer(P)+12)^ := 0;
|
|
Inc(Integer(P), sizeof(Variant));
|
|
Dec(elemCount);
|
|
end;
|
|
tkArray:
|
|
begin
|
|
FT := PFieldTable(Integer(typeInfo) + Byte(PTypeInfo(typeInfo).Name[0]));
|
|
while elemCount > 0 do
|
|
begin
|
|
_InitializeArray(P, FT.Fields[0].TypeInfo^, FT.Count);
|
|
Inc(Integer(P), FT.Size);
|
|
Dec(elemCount);
|
|
end;
|
|
end;
|
|
tkRecord:
|
|
begin
|
|
FT := PFieldTable(Integer(typeInfo) + Byte(PTypeInfo(typeInfo).Name[0]));
|
|
while elemCount > 0 do
|
|
begin
|
|
_InitializeRecord(P, typeInfo);
|
|
Inc(Integer(P), FT.Size);
|
|
Dec(elemCount);
|
|
end;
|
|
end;
|
|
else
|
|
Error(reInvalidPtr);
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to data to be initialized }
|
|
{ EDX pointer to type info describing data }
|
|
{ ECX number of elements of that type }
|
|
|
|
TEST ECX, ECX
|
|
JZ @@zerolength
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EBX,EAX // PIC safe. See comment above
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
|
|
XOR EDX,EDX
|
|
MOV AL,[ESI]
|
|
MOV DL,[ESI+1]
|
|
XOR ECX,ECX
|
|
|
|
CMP AL,tkLString
|
|
JE @@LString
|
|
CMP AL,tkWString
|
|
JE @@WString
|
|
CMP AL,tkVariant
|
|
JE @@Variant
|
|
CMP AL,tkArray
|
|
JE @@Array
|
|
CMP AL,tkRecord
|
|
JE @@Record
|
|
CMP AL,tkInterface
|
|
JE @@Interface
|
|
CMP AL,tkDynArray
|
|
JE @@DynArray
|
|
MOV AL,reInvalidPtr
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
JMP Error
|
|
|
|
@@LString:
|
|
@@WString:
|
|
@@Interface:
|
|
@@DynArray:
|
|
MOV [EBX],ECX
|
|
ADD EBX,4
|
|
DEC EDI
|
|
JG @@LString
|
|
JMP @@exit
|
|
|
|
@@Variant:
|
|
MOV [EBX ],ECX
|
|
MOV [EBX+ 4],ECX
|
|
MOV [EBX+ 8],ECX
|
|
MOV [EBX+12],ECX
|
|
ADD EBX,16
|
|
DEC EDI
|
|
JG @@Variant
|
|
JMP @@exit
|
|
|
|
@@Array:
|
|
PUSH EBP
|
|
MOV EBP,EDX
|
|
@@ArrayLoop:
|
|
MOV EDX,[ESI+EBP+2+8] // address of destructable fields typeinfo
|
|
MOV EAX,EBX
|
|
ADD EBX,[ESI+EBP+2] // size in bytes of the array data
|
|
MOV ECX,[ESI+EBP+2+4] // number of destructable fields
|
|
MOV EDX,[EDX]
|
|
CALL _InitializeArray
|
|
DEC EDI
|
|
JG @@ArrayLoop
|
|
POP EBP
|
|
JMP @@exit
|
|
|
|
@@Record:
|
|
PUSH EBP
|
|
MOV EBP,EDX
|
|
@@RecordLoop:
|
|
MOV EAX,EBX
|
|
ADD EBX,[ESI+EBP+2]
|
|
MOV EDX,ESI
|
|
CALL _InitializeRecord
|
|
DEC EDI
|
|
JG @@RecordLoop
|
|
POP EBP
|
|
|
|
@@exit:
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
@@zerolength:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _Initialize(p: Pointer; typeInfo: Pointer);
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
_InitializeArray(p, typeInfo, 1);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,1
|
|
JMP _InitializeArray
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _FinalizeRecord(p: Pointer; typeInfo: Pointer);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
FT: PFieldTable;
|
|
I: Cardinal;
|
|
begin
|
|
FT := PFieldTable(Integer(typeInfo) + Byte(PTypeInfo(typeInfo).Name[0]));
|
|
for I := 0 to FT.Count-1 do
|
|
_FinalizeArray(Pointer(Cardinal(P) + FT.Fields[I].Offset), FT.Fields[I].TypeInfo^, 1);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to record to be finalized }
|
|
{ EDX pointer to type info }
|
|
|
|
XOR ECX,ECX
|
|
|
|
PUSH EBX
|
|
MOV CL,[EDX+1]
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV EBX,EAX
|
|
LEA ESI,[EDX+ECX+2+8]
|
|
MOV EDI,[EDX+ECX+2+4]
|
|
|
|
@@loop:
|
|
|
|
MOV EDX,[ESI]
|
|
MOV EAX,[ESI+4]
|
|
ADD EAX,EBX
|
|
MOV EDX,[EDX]
|
|
MOV ECX,1
|
|
CALL _FinalizeArray
|
|
ADD ESI,8
|
|
DEC EDI
|
|
JG @@loop
|
|
|
|
MOV EAX,EBX
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _FinalizeArray(p: Pointer; typeInfo: Pointer; elemCount: Cardinal);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
FT: PFieldTable;
|
|
begin
|
|
if elemCount = 0 then Exit;
|
|
case PTypeInfo(typeInfo).Kind of
|
|
tkLString: _LStrArrayClr(P^, elemCount);
|
|
tkWString: {X-_WStrArrayClr X+}WStrArrayClrProc(P^, elemCount);
|
|
tkVariant:
|
|
while elemCount > 0 do
|
|
begin
|
|
VarClrProc(P);
|
|
Inc(Integer(P), sizeof(Variant));
|
|
Dec(elemCount);
|
|
end;
|
|
tkArray:
|
|
begin
|
|
FT := PFieldTable(Integer(typeInfo) + Byte(PTypeInfo(typeInfo).Name[0]));
|
|
while elemCount > 0 do
|
|
begin
|
|
_FinalizeArray(P, FT.Fields[0].TypeInfo^, FT.Count);
|
|
Inc(Integer(P), FT.Size);
|
|
Dec(elemCount);
|
|
end;
|
|
end;
|
|
tkRecord:
|
|
begin
|
|
FT := PFieldTable(Integer(typeInfo) + Byte(PTypeInfo(typeInfo).Name[0]));
|
|
while elemCount > 0 do
|
|
begin
|
|
_FinalizeRecord(P, typeInfo);
|
|
Inc(Integer(P), FT.Size);
|
|
Dec(elemCount);
|
|
end;
|
|
end;
|
|
tkInterface:
|
|
while elemCount > 0 do
|
|
begin
|
|
_IntfClear(IInterface(P^));
|
|
Inc(Integer(P), 4);
|
|
Dec(elemCount);
|
|
end;
|
|
tkDynArray:
|
|
while elemCount > 0 do
|
|
begin
|
|
_DynArrayClr(P);
|
|
Inc(Integer(P), 4);
|
|
Dec(elemCount);
|
|
end;
|
|
else
|
|
Error(reInvalidPtr);
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX pointer to data to be finalized }
|
|
{ EDX pointer to type info describing data }
|
|
{ ECX number of elements of that type }
|
|
|
|
{ This code appears to be PIC safe. The functions called from
|
|
here either don't make external calls or call Pascal
|
|
routines that will fix up EBX in their prolog code
|
|
(FreeMem, VarClr, IntfClr). }
|
|
|
|
CMP ECX, 0 { no array -> nop }
|
|
JE @@zerolength
|
|
|
|
PUSH EAX
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
|
|
XOR EDX,EDX
|
|
MOV AL,[ESI]
|
|
MOV DL,[ESI+1]
|
|
|
|
CMP AL,tkLString
|
|
JE @@LString
|
|
|
|
CMP AL,tkWString
|
|
JE @@WString
|
|
|
|
CMP AL,tkVariant
|
|
JE @@Variant
|
|
|
|
CMP AL,tkArray
|
|
JE @@Array
|
|
|
|
CMP AL,tkRecord
|
|
JE @@Record
|
|
|
|
CMP AL,tkInterface
|
|
JE @@Interface
|
|
|
|
CMP AL,tkDynArray
|
|
JE @@DynArray
|
|
|
|
JMP @@error
|
|
|
|
@@LString:
|
|
CMP ECX,1
|
|
MOV EAX,EBX
|
|
JG @@LStringArray
|
|
CALL _LStrClr
|
|
JMP @@exit
|
|
@@LStringArray:
|
|
MOV EDX,ECX
|
|
CALL _LStrArrayClr
|
|
JMP @@exit
|
|
|
|
@@WString:
|
|
CMP ECX,1
|
|
MOV EAX,EBX
|
|
JG @@WStringArray
|
|
//CALL _WStrClr {X}
|
|
CALL [WStrClrProc] {X}
|
|
JMP @@exit
|
|
@@WStringArray:
|
|
MOV EDX,ECX
|
|
//CALL _WStrArrayClr {X}
|
|
CALL [WStrArrayClrProc] {X}
|
|
JMP @@exit
|
|
@@Variant:
|
|
MOV EAX,EBX
|
|
ADD EBX,16
|
|
CALL _VarClr
|
|
DEC EDI
|
|
JG @@Variant
|
|
JMP @@exit
|
|
@@Array:
|
|
PUSH EBP
|
|
MOV EBP,EDX
|
|
@@ArrayLoop:
|
|
MOV EDX,[ESI+EBP+2+8]
|
|
MOV EAX,EBX
|
|
ADD EBX,[ESI+EBP+2]
|
|
MOV ECX,[ESI+EBP+2+4]
|
|
MOV EDX,[EDX]
|
|
CALL _FinalizeArray
|
|
DEC EDI
|
|
JG @@ArrayLoop
|
|
POP EBP
|
|
JMP @@exit
|
|
|
|
@@Record:
|
|
PUSH EBP
|
|
MOV EBP,EDX
|
|
@@RecordLoop:
|
|
{ inv: EDI = number of array elements to finalize }
|
|
|
|
MOV EAX,EBX
|
|
ADD EBX,[ESI+EBP+2]
|
|
MOV EDX,ESI
|
|
CALL _FinalizeRecord
|
|
DEC EDI
|
|
JG @@RecordLoop
|
|
POP EBP
|
|
JMP @@exit
|
|
|
|
@@Interface:
|
|
MOV EAX,EBX
|
|
ADD EBX,4
|
|
CALL _IntfClear
|
|
DEC EDI
|
|
JG @@Interface
|
|
JMP @@exit
|
|
|
|
@@DynArray:
|
|
MOV EAX,EBX
|
|
MOV EDX,ESI
|
|
ADD EBX,4
|
|
CALL _DynArrayClear
|
|
DEC EDI
|
|
JG @@DynArray
|
|
JMP @@exit
|
|
|
|
@@error:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
POP EAX
|
|
MOV AL,reInvalidPtr
|
|
JMP Error
|
|
|
|
@@exit:
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
POP EAX
|
|
@@zerolength:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _Finalize(p: Pointer; typeInfo: Pointer);
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
_FinalizeArray(p, typeInfo, 1);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,1
|
|
JMP _FinalizeArray
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _AddRefRecord{ p: Pointer; typeInfo: Pointer };
|
|
asm
|
|
{ -> EAX pointer to record to be referenced }
|
|
{ EDX pointer to type info }
|
|
|
|
XOR ECX,ECX
|
|
|
|
PUSH EBX
|
|
MOV CL,[EDX+1]
|
|
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
MOV EBX,EAX
|
|
LEA ESI,[EDX+ECX+2+8]
|
|
MOV EDI,[EDX+ECX+2+4]
|
|
|
|
@@loop:
|
|
|
|
MOV EDX,[ESI]
|
|
MOV EAX,[ESI+4]
|
|
ADD EAX,EBX
|
|
MOV EDX,[EDX]
|
|
MOV ECX, 1
|
|
CALL _AddRefArray
|
|
ADD ESI,8
|
|
DEC EDI
|
|
JG @@loop
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
{X}procedure DummyProc;
|
|
{X}begin
|
|
{X}end;
|
|
|
|
procedure _AddRefArray{ p: Pointer; typeInfo: Pointer; elemCount: Longint};
|
|
asm
|
|
{ -> EAX pointer to data to be referenced }
|
|
{ EDX pointer to type info describing data }
|
|
{ ECX number of elements of that type }
|
|
|
|
{ This code appears to be PIC safe. The functions called from
|
|
here either don't make external calls (LStrAddRef, WStrAddRef) or
|
|
are Pascal routines that will fix up EBX in their prolog code
|
|
(VarAddRef, IntfAddRef). }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
|
|
TEST ECX,ECX
|
|
JZ @@exit
|
|
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
|
|
XOR EDX,EDX
|
|
MOV AL,[ESI]
|
|
MOV DL,[ESI+1]
|
|
|
|
CMP AL,tkLString
|
|
JE @@LString
|
|
CMP AL,tkWString
|
|
JE @@WString
|
|
CMP AL,tkVariant
|
|
JE @@Variant
|
|
CMP AL,tkArray
|
|
JE @@Array
|
|
CMP AL,tkRecord
|
|
JE @@Record
|
|
CMP AL,tkInterface
|
|
JE @@Interface
|
|
CMP AL,tkDynArray
|
|
JE @@DynArray
|
|
MOV AL,reInvalidPtr
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
JMP Error
|
|
|
|
@@LString:
|
|
MOV EAX,[EBX]
|
|
ADD EBX,4
|
|
CALL _LStrAddRef
|
|
DEC EDI
|
|
JG @@LString
|
|
JMP @@exit
|
|
|
|
@@WString:
|
|
MOV EAX,EBX
|
|
ADD EBX,4
|
|
//CALL _WStrAddRef
|
|
CALL [WStrAddRefProc]
|
|
DEC EDI
|
|
JG @@WString
|
|
JMP @@exit
|
|
@@Variant:
|
|
MOV EAX,EBX
|
|
ADD EBX,16
|
|
CALL _VarAddRef
|
|
DEC EDI
|
|
JG @@Variant
|
|
JMP @@exit
|
|
|
|
@@Array:
|
|
PUSH EBP
|
|
MOV EBP,EDX
|
|
@@ArrayLoop:
|
|
MOV EDX,[ESI+EBP+2+8]
|
|
MOV EAX,EBX
|
|
ADD EBX,[ESI+EBP+2]
|
|
MOV ECX,[ESI+EBP+2+4]
|
|
MOV EDX,[EDX]
|
|
CALL _AddRefArray
|
|
DEC EDI
|
|
JG @@ArrayLoop
|
|
POP EBP
|
|
JMP @@exit
|
|
|
|
@@Record:
|
|
PUSH EBP
|
|
MOV EBP,EDX
|
|
@@RecordLoop:
|
|
MOV EAX,EBX
|
|
ADD EBX,[ESI+EBP+2]
|
|
MOV EDX,ESI
|
|
CALL _AddRefRecord
|
|
DEC EDI
|
|
JG @@RecordLoop
|
|
POP EBP
|
|
JMP @@exit
|
|
|
|
@@Interface:
|
|
MOV EAX,[EBX]
|
|
ADD EBX,4
|
|
CALL _IntfAddRef
|
|
DEC EDI
|
|
JG @@Interface
|
|
JMP @@exit
|
|
|
|
@@DynArray:
|
|
MOV EAX,[EBX]
|
|
ADD EBX,4
|
|
CALL _DynArrayAddRef
|
|
DEC EDI
|
|
JG @@DynArray
|
|
@@exit:
|
|
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
procedure _AddRef{ p: Pointer; typeInfo: Pointer};
|
|
asm
|
|
MOV ECX,1
|
|
JMP _AddRefArray
|
|
end;
|
|
|
|
|
|
procedure _CopyRecord{ dest, source, typeInfo: Pointer };
|
|
asm
|
|
{ -> EAX pointer to dest }
|
|
{ EDX pointer to source }
|
|
{ ECX pointer to typeInfo }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBP
|
|
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
|
|
XOR EAX,EAX
|
|
MOV AL,[ECX+1]
|
|
|
|
LEA EDI,[ECX+EAX+2+8]
|
|
MOV EBP,[EDI-4]
|
|
XOR EAX,EAX
|
|
MOV ECX,[EDI-8]
|
|
PUSH ECX
|
|
@@loop:
|
|
MOV ECX,[EDI+4]
|
|
SUB ECX,EAX
|
|
JLE @@nomove1
|
|
MOV EDX,EAX
|
|
ADD EAX,ESI
|
|
ADD EDX,EBX
|
|
CALL Move
|
|
@@noMove1:
|
|
MOV EAX,[EDI+4]
|
|
|
|
MOV EDX,[EDI]
|
|
MOV EDX,[EDX]
|
|
MOV CL,[EDX]
|
|
|
|
CMP CL,tkLString
|
|
JE @@LString
|
|
CMP CL,tkWString
|
|
JE @@WString
|
|
CMP CL,tkVariant
|
|
JE @@Variant
|
|
CMP CL,tkArray
|
|
JE @@Array
|
|
CMP CL,tkRecord
|
|
JE @@Record
|
|
CMP CL,tkInterface
|
|
JE @@Interface
|
|
CMP CL,tkDynArray
|
|
JE @@DynArray
|
|
MOV AL,reInvalidPtr
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
JMP Error
|
|
|
|
@@LString:
|
|
MOV EDX,[ESI+EAX]
|
|
ADD EAX,EBX
|
|
CALL _LStrAsg
|
|
MOV EAX,4
|
|
JMP @@common
|
|
|
|
@@WString:
|
|
MOV EDX,[ESI+EAX]
|
|
ADD EAX,EBX
|
|
CALL _WStrAsg
|
|
MOV EAX,4
|
|
JMP @@common
|
|
|
|
@@Variant:
|
|
LEA EDX,[ESI+EAX]
|
|
ADD EAX,EBX
|
|
CALL _VarCopy
|
|
MOV EAX,16
|
|
JMP @@common
|
|
|
|
@@Array:
|
|
XOR ECX,ECX
|
|
MOV CL,[EDX+1]
|
|
PUSH dword ptr [EDX+ECX+2]
|
|
PUSH dword ptr [EDX+ECX+2+4]
|
|
MOV ECX,[EDX+ECX+2+8]
|
|
MOV ECX,[ECX]
|
|
LEA EDX,[ESI+EAX]
|
|
ADD EAX,EBX
|
|
CALL _CopyArray
|
|
POP EAX
|
|
JMP @@common
|
|
|
|
@@Record:
|
|
XOR ECX,ECX
|
|
MOV CL,[EDX+1]
|
|
MOV ECX,[EDX+ECX+2]
|
|
PUSH ECX
|
|
MOV ECX,EDX
|
|
LEA EDX,[ESI+EAX]
|
|
ADD EAX,EBX
|
|
CALL _CopyRecord
|
|
POP EAX
|
|
JMP @@common
|
|
|
|
@@Interface:
|
|
MOV EDX,[ESI+EAX]
|
|
ADD EAX,EBX
|
|
CALL _IntfCopy
|
|
MOV EAX,4
|
|
JMP @@common
|
|
|
|
@@DynArray:
|
|
MOV ECX,EDX
|
|
MOV EDX,[ESI+EAX]
|
|
ADD EAX,EBX
|
|
CALL _DynArrayAsg
|
|
MOV EAX,4
|
|
|
|
@@common:
|
|
ADD EAX,[EDI+4]
|
|
ADD EDI,8
|
|
DEC EBP
|
|
JNZ @@loop
|
|
|
|
POP ECX
|
|
SUB ECX,EAX
|
|
JLE @@noMove2
|
|
LEA EDX,[EBX+EAX]
|
|
ADD EAX,ESI
|
|
CALL Move
|
|
@@noMove2:
|
|
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
procedure _CopyObject{ dest, source: Pointer; vmtPtrOffs: Longint; typeInfo: Pointer };
|
|
asm
|
|
{ -> EAX pointer to dest }
|
|
{ EDX pointer to source }
|
|
{ ECX offset of vmt in object }
|
|
{ [ESP+4] pointer to typeInfo }
|
|
|
|
ADD ECX,EAX { pointer to dest vmt }
|
|
PUSH dword ptr [ECX] { save dest vmt }
|
|
PUSH ECX
|
|
MOV ECX,[ESP+4+4+4]
|
|
CALL _CopyRecord
|
|
POP ECX
|
|
POP dword ptr [ECX] { restore dest vmt }
|
|
RET 4
|
|
|
|
end;
|
|
|
|
procedure _CopyArray{ dest, source, typeInfo: Pointer; cnt: Integer };
|
|
asm
|
|
{ -> EAX pointer to dest }
|
|
{ EDX pointer to source }
|
|
{ ECX pointer to typeInfo }
|
|
{ [ESP+4] count }
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBP
|
|
|
|
MOV EBX,EAX
|
|
MOV ESI,EDX
|
|
MOV EDI,ECX
|
|
MOV EBP,[ESP+4+4*4]
|
|
|
|
MOV CL,[EDI]
|
|
|
|
CMP CL,tkLString
|
|
JE @@LString
|
|
CMP CL,tkWString
|
|
JE @@WString
|
|
CMP CL,tkVariant
|
|
JE @@Variant
|
|
CMP CL,tkArray
|
|
JE @@Array
|
|
CMP CL,tkRecord
|
|
JE @@Record
|
|
CMP CL,tkInterface
|
|
JE @@Interface
|
|
CMP CL,tkDynArray
|
|
JE @@DynArray
|
|
MOV AL,reInvalidPtr
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
JMP Error
|
|
|
|
@@LString:
|
|
MOV EAX,EBX
|
|
MOV EDX,[ESI]
|
|
CALL _LStrAsg
|
|
ADD EBX,4
|
|
ADD ESI,4
|
|
DEC EBP
|
|
JNE @@LString
|
|
JMP @@exit
|
|
|
|
@@WString:
|
|
MOV EAX,EBX
|
|
MOV EDX,[ESI]
|
|
CALL _WStrAsg
|
|
ADD EBX,4
|
|
ADD ESI,4
|
|
DEC EBP
|
|
JNE @@WString
|
|
JMP @@exit
|
|
|
|
@@Variant:
|
|
MOV EAX,EBX
|
|
MOV EDX,ESI
|
|
CALL _VarCopy
|
|
ADD EBX,16
|
|
ADD ESI,16
|
|
DEC EBP
|
|
JNE @@Variant
|
|
JMP @@exit
|
|
|
|
@@Array:
|
|
XOR ECX,ECX
|
|
MOV CL,[EDI+1]
|
|
LEA EDI,[EDI+ECX+2]
|
|
@@ArrayLoop:
|
|
MOV EAX,EBX
|
|
MOV EDX,ESI
|
|
MOV ECX,[EDI+8]
|
|
PUSH dword ptr [EDI+4]
|
|
CALL _CopyArray
|
|
ADD EBX,[EDI]
|
|
ADD ESI,[EDI]
|
|
DEC EBP
|
|
JNE @@ArrayLoop
|
|
JMP @@exit
|
|
|
|
@@Record:
|
|
MOV EAX,EBX
|
|
MOV EDX,ESI
|
|
MOV ECX,EDI
|
|
CALL _CopyRecord
|
|
XOR EAX,EAX
|
|
MOV AL,[EDI+1]
|
|
ADD EBX,[EDI+EAX+2]
|
|
ADD ESI,[EDI+EAX+2]
|
|
DEC EBP
|
|
JNE @@Record
|
|
JMP @@exit
|
|
|
|
@@Interface:
|
|
MOV EAX,EBX
|
|
MOV EDX,[ESI]
|
|
CALL _IntfCopy
|
|
ADD EBX,4
|
|
ADD ESI,4
|
|
DEC EBP
|
|
JNE @@Interface
|
|
JMP @@exit
|
|
|
|
@@DynArray:
|
|
MOV EAX,EBX
|
|
MOV EDX,[ESI]
|
|
MOV ECX,EDI
|
|
CALL _DynArrayAsg
|
|
ADD EBX,4
|
|
ADD ESI,4
|
|
DEC EBP
|
|
JNE @@DynArray
|
|
|
|
@@exit:
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
RET 4
|
|
end;
|
|
|
|
|
|
function _New(size: Longint; typeInfo: Pointer): Pointer;
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
GetMem(Result, size);
|
|
if Result <> nil then
|
|
_Initialize(Result, typeInfo);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX size of object to allocate }
|
|
{ EDX pointer to typeInfo }
|
|
|
|
PUSH EDX
|
|
CALL _GetMem
|
|
POP EDX
|
|
TEST EAX,EAX
|
|
JE @@exit
|
|
PUSH EAX
|
|
CALL _Initialize
|
|
POP EAX
|
|
@@exit:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _Dispose(p: Pointer; typeInfo: Pointer);
|
|
{$IFDEF PUREPASCAL}
|
|
begin
|
|
_Finalize(p, typeinfo);
|
|
FreeMem(p);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{ -> EAX Pointer to object to be disposed }
|
|
{ EDX Pointer to type info }
|
|
|
|
PUSH EAX
|
|
CALL _Finalize
|
|
POP EAX
|
|
CALL _FreeMem
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ Wide character support }
|
|
{ ----------------------------------------------------- }
|
|
|
|
function WideCharToString(Source: PWideChar): string;
|
|
begin
|
|
WideCharToStrVar(Source, Result);
|
|
end;
|
|
|
|
function WideCharLenToString(Source: PWideChar; SourceLen: Integer): string;
|
|
begin
|
|
WideCharLenToStrVar(Source, SourceLen, Result);
|
|
end;
|
|
|
|
procedure WideCharToStrVar(Source: PWideChar; var Dest: string);
|
|
begin
|
|
_LStrFromPWChar(Dest, Source);
|
|
end;
|
|
|
|
procedure WideCharLenToStrVar(Source: PWideChar; SourceLen: Integer;
|
|
var Dest: string);
|
|
begin
|
|
_LStrFromPWCharLen(Dest, Source, SourceLen);
|
|
end;
|
|
|
|
function StringToWideChar(const Source: string; Dest: PWideChar;
|
|
DestSize: Integer): PWideChar;
|
|
begin
|
|
Dest[WCharFromChar(Dest, DestSize - 1, PChar(Source), Length(Source))] := #0;
|
|
Result := Dest;
|
|
end;
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ OLE string support }
|
|
{ ----------------------------------------------------- }
|
|
|
|
function OleStrToString(Source: PWideChar): string;
|
|
begin
|
|
OleStrToStrVar(Source, Result);
|
|
end;
|
|
|
|
procedure OleStrToStrVar(Source: PWideChar; var Dest: string);
|
|
begin
|
|
WideCharLenToStrVar(Source, Length(WideString(Pointer(Source))), Dest);
|
|
end;
|
|
|
|
function StringToOleStr(const Source: string): PWideChar;
|
|
begin
|
|
Result := nil;
|
|
_WStrFromPCharLen(WideString(Pointer(Result)), PChar(Pointer(Source)), Length(Source));
|
|
end;
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ Variant manager support }
|
|
{ ----------------------------------------------------- }
|
|
|
|
var
|
|
VariantManager: TVariantManager;
|
|
|
|
procedure VariantSystemUndefinedError;
|
|
asm
|
|
MOV AL,reVarInvalidOp
|
|
JMP Error;
|
|
end;
|
|
|
|
procedure VariantSystemDefaultVarClear(var V: TVarData);
|
|
begin
|
|
case V.VType of
|
|
varEmpty, varNull, varError:;
|
|
else
|
|
VariantSystemUndefinedError;
|
|
end;
|
|
end;
|
|
|
|
procedure InitVariantManager;
|
|
type
|
|
TPtrArray = array [Word] of Pointer;
|
|
var
|
|
P: ^TPtrArray;
|
|
I: Integer;
|
|
begin
|
|
P := @VariantManager;
|
|
for I := 0 to (SizeOf(VariantManager) div SizeOf(Pointer))-1 do
|
|
P[I] := @VariantSystemUndefinedError;
|
|
VariantManager.VarClear := @VariantSystemDefaultVarClear;
|
|
end;
|
|
|
|
procedure GetVariantManager(var VarMgr: TVariantManager);
|
|
begin
|
|
VarMgr := VariantManager;
|
|
end;
|
|
|
|
procedure SetVariantManager(const VarMgr: TVariantManager);
|
|
begin
|
|
VariantManager := VarMgr;
|
|
end;
|
|
|
|
function IsVariantManagerSet: Boolean;
|
|
type
|
|
TPtrArray = array [Word] of Pointer;
|
|
var
|
|
P: ^TPtrArray;
|
|
I: Integer;
|
|
begin
|
|
Result := True;
|
|
P := @VariantManager;
|
|
for I := 0 to (SizeOf(VariantManager) div SizeOf(Pointer))-1 do
|
|
if P[I] <> @VariantSystemUndefinedError then
|
|
begin
|
|
Result := False;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ Variant support }
|
|
{ ----------------------------------------------------- }
|
|
|
|
procedure _DispInvoke;//(var Dest: Variant; const Source: Variant;
|
|
//CallDesc: PCallDesc; Params: Pointer); cdecl;
|
|
asm
|
|
{$IFDEF PIC}
|
|
CALL GetGOT
|
|
LEA EAX,[EAX].OFFSET VariantManager
|
|
JMP [EAX].TVariantManager.DispInvoke
|
|
{$ELSE}
|
|
JMP VariantManager.DispInvoke
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarClear(var V : Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarClear(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarClear
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarCopy(var Dest: Variant; const Source: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarCopy(Dest, Source);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarCopy
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarCast(var Dest: Variant; const Source: Variant; VarType: Integer);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarCast(Dest, Source, VarType);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarCast
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarCastOle(var Dest: Variant; const Source: Variant; VarType: Integer);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarCastOle(Dest, Source, VarType);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarCastOle
|
|
{$IFEND}
|
|
end;
|
|
|
|
function _VarToInt(const V: Variant): Integer;
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
Result := VariantManager.VarToInt(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToInt
|
|
{$IFEND}
|
|
end;
|
|
|
|
function _VarToInt64(const V: Variant): Int64;
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
Result := VariantManager.VarToInt64(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToInt64
|
|
{$IFEND}
|
|
end;
|
|
|
|
function _VarToBool(const V: Variant): Boolean;
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
Result := VariantManager.VarToBool(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToBool
|
|
{$IFEND}
|
|
end;
|
|
|
|
function _VarToReal(const V: Variant): Extended;
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
Result := VariantManager.VarToReal(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToReal
|
|
{$IFEND}
|
|
end;
|
|
|
|
function _VarToCurr(const V: Variant): Currency;
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
Result := VariantManager.VarToCurr(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToCurr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarToPStr(var S; const V: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarToPStr(S, V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToPStr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarToLStr(var S: string; const V: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarToLStr(S, V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToLStr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarToWStr(var S: WideString; const V: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarToWStr(S, V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToWStr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarToIntf(var Unknown: IInterface; const V: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarToIntf(Unknown, V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToIntf
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarToDisp(var Dispatch: IDispatch; const V: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarToDisp(Dispatch, V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToDisp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarToDynArray(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarToDynArray(DynArray, V, TypeInfo);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarToDynArray
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarFromInt(var V: Variant; const Value, Range: Integer);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarFromInt(V, Value, Range);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarFromInt
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarFromInt64(var V: Variant; const Value: Int64);
|
|
begin
|
|
VariantManager.VarFromInt64(V, Value);
|
|
end;
|
|
|
|
procedure _VarFromBool(var V: Variant; const Value: Boolean);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarFromBool(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarFromBool
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarFromReal; // var V: Variant; const Value: Real
|
|
asm
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL GetGOT
|
|
POP ECX
|
|
LEA EAX,[EAX].OFFSET VariantManager
|
|
MOV EAX,[EAX].TVariantManager.VarFromReal
|
|
XCHG EAX,[ESP]
|
|
RET
|
|
{$ELSE}
|
|
JMP VariantManager.VarFromReal
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarFromTDateTime; // var V: Variant; const Value: TDateTime
|
|
asm
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL GetGOT
|
|
POP ECX
|
|
LEA EAX,[EAX].OFFSET VariantManager
|
|
MOV EAX,[EAX].TVariantManager.VarFromTDateTime
|
|
XCHG EAX,[ESP]
|
|
RET
|
|
{$ELSE}
|
|
JMP VariantManager.VarFromTDateTime
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarFromCurr; // var V: Variant; const Value: Currency
|
|
asm
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL GetGOT
|
|
POP ECX
|
|
LEA EAX,[EAX].OFFSET VariantManager
|
|
MOV EAX,[EAX].TVariantManager.VarFromCurr
|
|
XCHG EAX,[ESP]
|
|
RET
|
|
{$ELSE}
|
|
JMP VariantManager.VarFromCurr
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarFromPStr(var V: Variant; const Value: ShortString);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarFromPStr(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarFromPStr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarFromLStr(var V: Variant; const Value: string);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarFromLStr(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarFromLStr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarFromWStr(var V: Variant; const Value: WideString);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarFromWStr(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarFromWStr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarFromIntf(var V: Variant; const Value: IInterface);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarFromIntf(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarFromIntf
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarFromDisp(var V: Variant; const Value: IDispatch);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarFromDisp(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarFromDisp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarFromDynArray(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarFromDynArray(V, DynArray, TypeInfo);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarFromDynArray
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _OleVarFromPStr(var V: OleVariant; const Value: ShortString);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.OleVarFromPStr(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.OleVarFromPStr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _OleVarFromLStr(var V: OleVariant; const Value: string);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.OleVarFromLStr(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.OleVarFromLStr
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _OleVarFromVar(var V: OleVariant; const Value: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.OleVarFromVar(V, Value);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.OleVarFromVar
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _OleVarFromInt(var V: OleVariant; const Value, Range: Integer);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.OleVarFromInt(V, Value, Range);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.OleVarFromInt
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarAdd(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opAdd);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opAdd
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarSub(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opSubtract);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opSubtract
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarMul(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opMultiply);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opMultiply
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarDiv(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opIntDivide);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opIntDivide
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarMod(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opModulus);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opModulus
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarAnd(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opAnd);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opAnd
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarOr(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opOr);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opOr
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarXor(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opXor);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opXor
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarShl(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opShiftLeft);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opShiftLeft
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarShr(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opShiftRight);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opShiftRight
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarRDiv(var Left: Variant; const Right: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarOp(Left, Right, opDivide);
|
|
{$ELSE}
|
|
asm
|
|
MOV ECX,opDivide
|
|
JMP VariantManager.VarOp
|
|
{$IFEND}
|
|
end;
|
|
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
// result is set in the flags
|
|
procedure DoVarCmp(const Left, Right: Variant; OpCode: Integer);
|
|
begin
|
|
VariantManager.VarCmp(TVarData(Left), TVarData(Right), OpCode);
|
|
end;
|
|
{$IFEND}
|
|
|
|
procedure _VarCmpEQ(const Left, Right: Variant); // result is set in the flags
|
|
asm
|
|
MOV ECX, opCmpEQ
|
|
{$IFDEF PIC}
|
|
JMP DoVarCmp
|
|
{$ELSE}
|
|
JMP VariantManager.VarCmp
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarCmpNE(const Left, Right: Variant); // result is set in the flags
|
|
asm
|
|
MOV ECX, opCmpNE
|
|
{$IFDEF PIC}
|
|
JMP DoVarCmp
|
|
{$ELSE}
|
|
JMP VariantManager.VarCmp
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarCmpLT(const Left, Right: Variant); // result is set in the flags
|
|
asm
|
|
MOV ECX, opCmpLT
|
|
{$IFDEF PIC}
|
|
JMP DoVarCmp
|
|
{$ELSE}
|
|
JMP VariantManager.VarCmp
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarCmpLE(const Left, Right: Variant); // result is set in the flags
|
|
asm
|
|
MOV ECX, opCmpLE
|
|
{$IFDEF PIC}
|
|
JMP DoVarCmp
|
|
{$ELSE}
|
|
JMP VariantManager.VarCmp
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarCmpGT(const Left, Right: Variant); // result is set in the flags
|
|
asm
|
|
MOV ECX, opCmpGT
|
|
{$IFDEF PIC}
|
|
JMP DoVarCmp
|
|
{$ELSE}
|
|
JMP VariantManager.VarCmp
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarCmpGE(const Left, Right: Variant); // result is set in the flags
|
|
asm
|
|
MOV ECX, opCmpGE
|
|
{$IFDEF PIC}
|
|
JMP DoVarCmp
|
|
{$ELSE}
|
|
JMP VariantManager.VarCmp
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
procedure _VarNeg(var V: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarNeg(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarNeg
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarNot(var V: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarNot(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarNot
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _VarCopyNoInd;
|
|
asm
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL GetGOT
|
|
POP ECX
|
|
LEA EAX,[EAX].OFFSET VariantManager
|
|
MOV EAX,[EAX].TVariantManager.VarCopyNoInd
|
|
XCHG EAX,[ESP]
|
|
RET
|
|
{$ELSE}
|
|
JMP VariantManager.VarCopyNoInd
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarClr(var V: Variant);
|
|
asm
|
|
PUSH EAX
|
|
CALL _VarClear
|
|
POP EAX
|
|
end;
|
|
|
|
procedure _VarAddRef(var V: Variant);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarAddRef(V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarAddRef
|
|
{$IFEND}
|
|
end;
|
|
|
|
procedure _IntfDispCall;
|
|
asm
|
|
{$IFDEF PIC}
|
|
PUSH EAX
|
|
PUSH ECX
|
|
CALL GetGOT
|
|
POP ECX
|
|
LEA EAX,[EAX].OFFSET DispCallByIDProc
|
|
MOV EAX,[EAX]
|
|
XCHG EAX,[ESP]
|
|
RET
|
|
{$ELSE}
|
|
JMP DispCallByIDProc
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _DispCallByIDError;
|
|
asm
|
|
MOV AL,reVarDispatch
|
|
JMP Error
|
|
end;
|
|
|
|
procedure _IntfVarCall;
|
|
asm
|
|
end;
|
|
|
|
function _WriteVariant(var T: Text; const V: Variant; Width: Integer): Pointer;
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
Result := VariantManager.WriteVariant(T, V, Width);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.WriteVariant
|
|
{$IFEND}
|
|
end;
|
|
|
|
function _Write0Variant(var T: Text; const V: Variant): Pointer;
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
Result := VariantManager.Write0Variant(T, V);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.Write0Variant
|
|
{$IFEND}
|
|
end;
|
|
|
|
{ ----------------------------------------------------- }
|
|
{ Variant array support }
|
|
{ ----------------------------------------------------- }
|
|
|
|
procedure _VarArrayRedim(var A : Variant; HighBound: Integer);
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
VariantManager.VarArrayRedim(A, HighBound);
|
|
{$ELSE}
|
|
asm
|
|
JMP VariantManager.VarArrayRedim
|
|
{$IFEND}
|
|
end;
|
|
|
|
function _VarArrayGet(var A: Variant; IndexCount: Integer;
|
|
Indices: Integer): Variant; cdecl;
|
|
asm
|
|
POP EBP
|
|
{$IFDEF PIC}
|
|
CALL GetGOT
|
|
LEA EAX,[EAX].OFFSET VariantManager
|
|
MOV EAX,[EAX].TVariantManager.VarArrayGet
|
|
PUSH EAX
|
|
RET
|
|
{$ELSE}
|
|
JMP VariantManager.VarArrayGet
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure _VarArrayPut(var A: Variant; const Value: Variant;
|
|
IndexCount: Integer; Indices: Integer); cdecl;
|
|
asm
|
|
POP EBP
|
|
{$IFDEF PIC}
|
|
CALL GetGOT
|
|
LEA EAX,[EAX].OFFSET VariantManager
|
|
MOV EAX,[EAX].TVariantManager.VarArrayPut
|
|
PUSH EAX
|
|
RET
|
|
{$ELSE}
|
|
JMP VariantManager.VarArrayPut
|
|
{$ENDIF}
|
|
end;
|
|
|
|
// 64 bit integer helper routines
|
|
//
|
|
// These functions always return the 64-bit result in EAX:EDX
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit signed multiply
|
|
// ------------------------------------------------------------------------------
|
|
//
|
|
// Param 1(EAX:EDX), Param 2([ESP+8]:[ESP+4]) ; before reg pushing
|
|
//
|
|
|
|
procedure __llmul;
|
|
asm
|
|
push edx
|
|
push eax
|
|
|
|
// Param2 : [ESP+16]:[ESP+12] (hi:lo)
|
|
// Param1 : [ESP+4]:[ESP] (hi:lo)
|
|
|
|
mov eax, [esp+16]
|
|
mul dword ptr [esp]
|
|
mov ecx, eax
|
|
|
|
mov eax, [esp+4]
|
|
mul dword ptr [esp+12]
|
|
add ecx, eax
|
|
|
|
mov eax, [esp]
|
|
mul dword ptr [esp+12]
|
|
add edx, ecx
|
|
|
|
pop ecx
|
|
pop ecx
|
|
|
|
ret 8
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit signed multiply, with overflow check (98.05.15: overflow not supported yet)
|
|
// ------------------------------------------------------------------------------
|
|
//
|
|
// Param1 ~= U (Uh, Ul)
|
|
// Param2 ~= V (Vh, Vl)
|
|
//
|
|
// Param 1(EAX:EDX), Param 2([ESP+8]:[ESP+4]) ; before reg pushing
|
|
//
|
|
// compiler-helper function
|
|
// O-flag set on exit => result is invalid
|
|
// O-flag clear on exit => result is valid
|
|
|
|
procedure __llmulo;
|
|
asm
|
|
push edx
|
|
push eax
|
|
|
|
// Param2 : [ESP+16]:[ESP+12] (hi:lo)
|
|
// Param1 : [ESP+4]:[ESP] (hi:lo)
|
|
|
|
mov eax, [esp+16]
|
|
mul dword ptr [esp]
|
|
mov ecx, eax
|
|
|
|
mov eax, [esp+4]
|
|
mul dword ptr [esp+12]
|
|
add ecx, eax
|
|
|
|
mov eax, [esp]
|
|
mul dword ptr [esp+12]
|
|
add edx, ecx
|
|
|
|
pop ecx
|
|
pop ecx
|
|
|
|
ret 8
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit signed division
|
|
// ------------------------------------------------------------------------------
|
|
|
|
//
|
|
// Dividend = Numerator, Divisor = Denominator
|
|
//
|
|
// Dividend(EAX:EDX), Divisor([ESP+8]:[ESP+4]) ; before reg pushing
|
|
//
|
|
//
|
|
|
|
procedure __lldiv;
|
|
asm
|
|
push ebp
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
xor edi,edi
|
|
|
|
mov ebx,20[esp] // get the divisor low dword
|
|
mov ecx,24[esp] // get the divisor high dword
|
|
|
|
or ecx,ecx
|
|
jnz @__lldiv@slow_ldiv // both high words are zero
|
|
|
|
or edx,edx
|
|
jz @__lldiv@quick_ldiv
|
|
|
|
or ebx,ebx
|
|
jz @__lldiv@quick_ldiv // if ecx:ebx == 0 force a zero divide
|
|
// we don't expect this to actually
|
|
// work
|
|
|
|
@__lldiv@slow_ldiv:
|
|
|
|
//
|
|
// Signed division should be done. Convert negative
|
|
// values to positive and do an unsigned division.
|
|
// Store the sign value in the next higher bit of
|
|
// di (test mask of 4). Thus when we are done, testing
|
|
// that bit will determine the sign of the result.
|
|
//
|
|
or edx,edx // test sign of dividend
|
|
jns @__lldiv@onepos
|
|
neg edx
|
|
neg eax
|
|
sbb edx,0 // negate dividend
|
|
or edi,1
|
|
|
|
@__lldiv@onepos:
|
|
or ecx,ecx // test sign of divisor
|
|
jns @__lldiv@positive
|
|
neg ecx
|
|
neg ebx
|
|
sbb ecx,0 // negate divisor
|
|
xor edi,1
|
|
|
|
@__lldiv@positive:
|
|
mov ebp,ecx
|
|
mov ecx,64 // shift counter
|
|
push edi // save the flags
|
|
//
|
|
// Now the stack looks something like this:
|
|
//
|
|
// 24[esp]: divisor (high dword)
|
|
// 20[esp]: divisor (low dword)
|
|
// 16[esp]: return EIP
|
|
// 12[esp]: previous EBP
|
|
// 8[esp]: previous EBX
|
|
// 4[esp]: previous ESI
|
|
// [esp]: previous EDI
|
|
//
|
|
xor edi,edi // fake a 64 bit dividend
|
|
xor esi,esi
|
|
|
|
@__lldiv@xloop:
|
|
shl eax,1 // shift dividend left one bit
|
|
rcl edx,1
|
|
rcl esi,1
|
|
rcl edi,1
|
|
cmp edi,ebp // dividend larger?
|
|
jb @__lldiv@nosub
|
|
ja @__lldiv@subtract
|
|
cmp esi,ebx // maybe
|
|
jb @__lldiv@nosub
|
|
|
|
@__lldiv@subtract:
|
|
sub esi,ebx
|
|
sbb edi,ebp // subtract the divisor
|
|
inc eax // build quotient
|
|
|
|
@__lldiv@nosub:
|
|
loop @__lldiv@xloop
|
|
//
|
|
// When done with the loop the four registers values' look like:
|
|
//
|
|
// | edi | esi | edx | eax |
|
|
// | remainder | quotient |
|
|
//
|
|
pop ebx // get control bits
|
|
test ebx,1 // needs negative
|
|
jz @__lldiv@finish
|
|
neg edx
|
|
neg eax
|
|
sbb edx,0 // negate
|
|
|
|
@__lldiv@finish:
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
ret 8
|
|
|
|
@__lldiv@quick_ldiv:
|
|
div ebx // unsigned divide
|
|
xor edx,edx
|
|
jmp @__lldiv@finish
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit signed division with overflow check (98.05.15: not implementated yet)
|
|
// ------------------------------------------------------------------------------
|
|
|
|
//
|
|
// Dividend = Numerator, Divisor = Denominator
|
|
//
|
|
// Dividend(EAX:EDX), Divisor([ESP+8]:[ESP+4])
|
|
// Param 1 (EAX:EDX), Param 2([ESP+8]:[ESP+4])
|
|
//
|
|
// Param1 ~= U (Uh, Ul)
|
|
// Param2 ~= V (Vh, Vl)
|
|
//
|
|
// compiler-helper function
|
|
// O-flag set on exit => result is invalid
|
|
// O-flag clear on exit => result is valid
|
|
//
|
|
|
|
procedure __lldivo;
|
|
asm
|
|
// check for overflow condition: min(int64) DIV -1
|
|
push esi
|
|
mov esi, [esp+12] // Vh
|
|
and esi, [esp+8] // Vl
|
|
cmp esi, 0ffffffffh // V = -1?
|
|
jne @@divok
|
|
|
|
mov esi, eax
|
|
or esi, edx
|
|
cmp esi, 80000000H // U = min(int64)?
|
|
jne @@divok
|
|
|
|
@@divOvl:
|
|
mov eax, esi
|
|
pop esi
|
|
dec eax // turn on O-flag
|
|
ret
|
|
|
|
@@divok:
|
|
pop esi
|
|
push dword ptr [esp+8] // Vh
|
|
push dword ptr [esp+8] // Vl (offset is changed from push)
|
|
call __lldiv
|
|
and eax, eax // turn off O-flag
|
|
ret 8
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit unsigned division
|
|
// ------------------------------------------------------------------------------
|
|
|
|
// Dividend(EAX(hi):EDX(lo)), Divisor([ESP+8](hi):[ESP+4](lo)) // before reg pushing
|
|
procedure __lludiv;
|
|
asm
|
|
push ebp
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
//
|
|
// Now the stack looks something like this:
|
|
//
|
|
// 24[esp]: divisor (high dword)
|
|
// 20[esp]: divisor (low dword)
|
|
// 16[esp]: return EIP
|
|
// 12[esp]: previous EBP
|
|
// 8[esp]: previous EBX
|
|
// 4[esp]: previous ESI
|
|
// [esp]: previous EDI
|
|
//
|
|
|
|
// dividend is pushed last, therefore the first in the args
|
|
// divisor next.
|
|
//
|
|
mov ebx,20[esp] // get the first low word
|
|
mov ecx,24[esp] // get the first high word
|
|
|
|
or ecx,ecx
|
|
jnz @__lludiv@slow_ldiv // both high words are zero
|
|
|
|
or edx,edx
|
|
jz @__lludiv@quick_ldiv
|
|
|
|
or ebx,ebx
|
|
jz @__lludiv@quick_ldiv // if ecx:ebx == 0 force a zero divide
|
|
// we don't expect this to actually
|
|
// work
|
|
|
|
@__lludiv@slow_ldiv:
|
|
mov ebp,ecx
|
|
mov ecx,64 // shift counter
|
|
xor edi,edi // fake a 64 bit dividend
|
|
xor esi,esi
|
|
|
|
@__lludiv@xloop:
|
|
shl eax,1 // shift dividend left one bit
|
|
rcl edx,1
|
|
rcl esi,1
|
|
rcl edi,1
|
|
cmp edi,ebp // dividend larger?
|
|
jb @__lludiv@nosub
|
|
ja @__lludiv@subtract
|
|
cmp esi,ebx // maybe
|
|
jb @__lludiv@nosub
|
|
|
|
@__lludiv@subtract:
|
|
sub esi,ebx
|
|
sbb edi,ebp // subtract the divisor
|
|
inc eax // build quotient
|
|
|
|
@__lludiv@nosub:
|
|
loop @__lludiv@xloop
|
|
//
|
|
// When done with the loop the four registers values' look like:
|
|
//
|
|
// | edi | esi | edx | eax |
|
|
// | remainder | quotient |
|
|
//
|
|
|
|
@__lludiv@finish:
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
ret 8
|
|
|
|
@__lludiv@quick_ldiv:
|
|
div ebx // unsigned divide
|
|
xor edx,edx
|
|
jmp @__lludiv@finish
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit modulo
|
|
// ------------------------------------------------------------------------------
|
|
|
|
// Dividend(EAX:EDX), Divisor([ESP+8]:[ESP+4]) // before reg pushing
|
|
procedure __llmod;
|
|
asm
|
|
push ebp
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
xor edi,edi
|
|
//
|
|
// dividend is pushed last, therefore the first in the args
|
|
// divisor next.
|
|
//
|
|
mov ebx,20[esp] // get the first low word
|
|
mov ecx,24[esp] // get the first high word
|
|
or ecx,ecx
|
|
jnz @__llmod@slow_ldiv // both high words are zero
|
|
|
|
or edx,edx
|
|
jz @__llmod@quick_ldiv
|
|
|
|
or ebx,ebx
|
|
jz @__llmod@quick_ldiv // if ecx:ebx == 0 force a zero divide
|
|
// we don't expect this to actually
|
|
// work
|
|
@__llmod@slow_ldiv:
|
|
//
|
|
// Signed division should be done. Convert negative
|
|
// values to positive and do an unsigned division.
|
|
// Store the sign value in the next higher bit of
|
|
// di (test mask of 4). Thus when we are done, testing
|
|
// that bit will determine the sign of the result.
|
|
//
|
|
or edx,edx // test sign of dividend
|
|
jns @__llmod@onepos
|
|
neg edx
|
|
neg eax
|
|
sbb edx,0 // negate dividend
|
|
or edi,1
|
|
|
|
@__llmod@onepos:
|
|
or ecx,ecx // test sign of divisor
|
|
jns @__llmod@positive
|
|
neg ecx
|
|
neg ebx
|
|
sbb ecx,0 // negate divisor
|
|
|
|
@__llmod@positive:
|
|
mov ebp,ecx
|
|
mov ecx,64 // shift counter
|
|
push edi // save the flags
|
|
//
|
|
// Now the stack looks something like this:
|
|
//
|
|
// 24[esp]: divisor (high dword)
|
|
// 20[esp]: divisor (low dword)
|
|
// 16[esp]: return EIP
|
|
// 12[esp]: previous EBP
|
|
// 8[esp]: previous EBX
|
|
// 4[esp]: previous ESI
|
|
// [esp]: previous EDI
|
|
//
|
|
xor edi,edi // fake a 64 bit dividend
|
|
xor esi,esi
|
|
|
|
@__llmod@xloop:
|
|
shl eax,1 // shift dividend left one bit
|
|
rcl edx,1
|
|
rcl esi,1
|
|
rcl edi,1
|
|
cmp edi,ebp // dividend larger?
|
|
jb @__llmod@nosub
|
|
ja @__llmod@subtract
|
|
cmp esi,ebx // maybe
|
|
jb @__llmod@nosub
|
|
|
|
@__llmod@subtract:
|
|
sub esi,ebx
|
|
sbb edi,ebp // subtract the divisor
|
|
inc eax // build quotient
|
|
|
|
@__llmod@nosub:
|
|
loop @__llmod@xloop
|
|
//
|
|
// When done with the loop the four registers values' look like:
|
|
//
|
|
// | edi | esi | edx | eax |
|
|
// | remainder | quotient |
|
|
//
|
|
mov eax,esi
|
|
mov edx,edi // use remainder
|
|
|
|
pop ebx // get control bits
|
|
test ebx,1 // needs negative
|
|
jz @__llmod@finish
|
|
neg edx
|
|
neg eax
|
|
sbb edx,0 // negate
|
|
|
|
@__llmod@finish:
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
ret 8
|
|
|
|
@__llmod@quick_ldiv:
|
|
div ebx // unsigned divide
|
|
xchg eax,edx
|
|
xor edx,edx
|
|
jmp @__llmod@finish
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit signed modulo with overflow (98.05.15: overflow not yet supported)
|
|
// ------------------------------------------------------------------------------
|
|
|
|
// Dividend(EAX:EDX), Divisor([ESP+8]:[ESP+4])
|
|
// Param 1 (EAX:EDX), Param 2([ESP+8]:[ESP+4])
|
|
//
|
|
// Param1 ~= U (Uh, Ul)
|
|
// Param2 ~= V (Vh, Vl)
|
|
//
|
|
// compiler-helper function
|
|
// O-flag set on exit => result is invalid
|
|
// O-flag clear on exit => result is valid
|
|
//
|
|
|
|
procedure __llmodo;
|
|
asm
|
|
// check for overflow condition: min(int64) MOD -1
|
|
push esi
|
|
mov esi, [esp+12] // Vh
|
|
and esi, [esp+8] // Vl
|
|
cmp esi, 0ffffffffh // V = -1?
|
|
jne @@modok
|
|
|
|
mov esi, eax
|
|
or esi, edx
|
|
cmp esi, 80000000H // U = min(int64)?
|
|
jne @@modok
|
|
|
|
@@modOvl:
|
|
mov eax, esi
|
|
pop esi
|
|
dec eax // turn on O-flag
|
|
ret
|
|
|
|
@@modok:
|
|
pop esi
|
|
push dword ptr [esp+8] // Vh
|
|
push dword ptr [esp+8] // Vl (offset is changed from push)
|
|
call __llmod
|
|
and eax, eax // turn off O-flag
|
|
ret 8
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit unsigned modulo
|
|
// ------------------------------------------------------------------------------
|
|
// Dividend(EAX(hi):EDX(lo)), Divisor([ESP+8](hi):[ESP+4](lo)) // before reg pushing
|
|
|
|
procedure __llumod;
|
|
asm
|
|
push ebp
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
//
|
|
// Now the stack looks something like this:
|
|
//
|
|
// 24[esp]: divisor (high dword)
|
|
// 20[esp]: divisor (low dword)
|
|
// 16[esp]: return EIP
|
|
// 12[esp]: previous EBP
|
|
// 8[esp]: previous EBX
|
|
// 4[esp]: previous ESI
|
|
// [esp]: previous EDI
|
|
//
|
|
|
|
// dividend is pushed last, therefore the first in the args
|
|
// divisor next.
|
|
//
|
|
mov ebx,20[esp] // get the first low word
|
|
mov ecx,24[esp] // get the first high word
|
|
or ecx,ecx
|
|
jnz @__llumod@slow_ldiv // both high words are zero
|
|
|
|
or edx,edx
|
|
jz @__llumod@quick_ldiv
|
|
|
|
or ebx,ebx
|
|
jz @__llumod@quick_ldiv // if ecx:ebx == 0 force a zero divide
|
|
// we don't expect this to actually
|
|
// work
|
|
@__llumod@slow_ldiv:
|
|
mov ebp,ecx
|
|
mov ecx,64 // shift counter
|
|
xor edi,edi // fake a 64 bit dividend
|
|
xor esi,esi //
|
|
|
|
@__llumod@xloop:
|
|
shl eax,1 // shift dividend left one bit
|
|
rcl edx,1
|
|
rcl esi,1
|
|
rcl edi,1
|
|
cmp edi,ebp // dividend larger?
|
|
jb @__llumod@nosub
|
|
ja @__llumod@subtract
|
|
cmp esi,ebx // maybe
|
|
jb @__llumod@nosub
|
|
|
|
@__llumod@subtract:
|
|
sub esi,ebx
|
|
sbb edi,ebp // subtract the divisor
|
|
inc eax // build quotient
|
|
|
|
@__llumod@nosub:
|
|
loop @__llumod@xloop
|
|
//
|
|
// When done with the loop the four registers values' look like:
|
|
//
|
|
// | edi | esi | edx | eax |
|
|
// | remainder | quotient |
|
|
//
|
|
mov eax,esi
|
|
mov edx,edi // use remainder
|
|
|
|
@__llumod@finish:
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
ret 8
|
|
|
|
@__llumod@quick_ldiv:
|
|
div ebx // unsigned divide
|
|
xchg eax,edx
|
|
xor edx,edx
|
|
jmp @__llumod@finish
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit shift left
|
|
// ------------------------------------------------------------------------------
|
|
|
|
//
|
|
// target (EAX:EDX) count (ECX)
|
|
//
|
|
procedure __llshl;
|
|
asm
|
|
cmp cl, 32
|
|
jl @__llshl@below32
|
|
cmp cl, 64
|
|
jl @__llshl@below64
|
|
xor edx, edx
|
|
xor eax, eax
|
|
ret
|
|
|
|
@__llshl@below64:
|
|
mov edx, eax
|
|
shl edx, cl
|
|
xor eax, eax
|
|
ret
|
|
|
|
@__llshl@below32:
|
|
shld edx, eax, cl
|
|
shl eax, cl
|
|
ret
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit signed shift right
|
|
// ------------------------------------------------------------------------------
|
|
// target (EAX:EDX) count (ECX)
|
|
|
|
procedure __llshr;
|
|
asm
|
|
cmp cl, 32
|
|
jl @__llshr@below32
|
|
cmp cl, 64
|
|
jl @__llshr@below64
|
|
sar edx, 1fh
|
|
mov eax,edx
|
|
ret
|
|
|
|
@__llshr@below64:
|
|
mov eax, edx
|
|
cdq
|
|
sar eax,cl
|
|
ret
|
|
|
|
@__llshr@below32:
|
|
shrd eax, edx, cl
|
|
sar edx, cl
|
|
ret
|
|
end;
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 64-bit unsigned shift right
|
|
// ------------------------------------------------------------------------------
|
|
|
|
// target (EAX:EDX) count (ECX)
|
|
procedure __llushr;
|
|
asm
|
|
cmp cl, 32
|
|
jl @__llushr@below32
|
|
cmp cl, 64
|
|
jl @__llushr@below64
|
|
xor edx, edx
|
|
xor eax, eax
|
|
ret
|
|
|
|
@__llushr@below64:
|
|
mov eax, edx
|
|
xor edx, edx
|
|
shr eax, cl
|
|
ret
|
|
|
|
@__llushr@below32:
|
|
shrd eax, edx, cl
|
|
shr edx, cl
|
|
ret
|
|
end;
|
|
|
|
function _StrInt64(val: Int64; width: Integer): ShortString;
|
|
var
|
|
d: array[0..31] of Char; { need 19 digits and a sign }
|
|
i, k: Integer;
|
|
sign: Boolean;
|
|
spaces: Integer;
|
|
begin
|
|
{ Produce an ASCII representation of the number in reverse order }
|
|
i := 0;
|
|
sign := val < 0;
|
|
repeat
|
|
d[i] := Chr( Abs(val mod 10) + Ord('0') );
|
|
Inc(i);
|
|
val := val div 10;
|
|
until val = 0;
|
|
if sign then
|
|
begin
|
|
d[i] := '-';
|
|
Inc(i);
|
|
end;
|
|
|
|
{ Fill the Result with the appropriate number of blanks }
|
|
if width > 255 then
|
|
width := 255;
|
|
k := 1;
|
|
spaces := width - i;
|
|
while k <= spaces do
|
|
begin
|
|
Result[k] := ' ';
|
|
Inc(k);
|
|
end;
|
|
|
|
{ Fill the Result with the number }
|
|
while i > 0 do
|
|
begin
|
|
Dec(i);
|
|
Result[k] := d[i];
|
|
Inc(k);
|
|
end;
|
|
|
|
{ Result is k-1 characters long }
|
|
SetLength(Result, k-1);
|
|
|
|
end;
|
|
|
|
function _Str0Int64(val: Int64): ShortString;
|
|
begin
|
|
Result := _StrInt64(val, 0);
|
|
end;
|
|
|
|
procedure _WriteInt64;
|
|
asm
|
|
{ PROCEDURE _WriteInt64( VAR t: Text; val: Int64; with: Longint); }
|
|
{ ->EAX Pointer to file record }
|
|
{ [ESP+4] Value }
|
|
{ EDX Field width }
|
|
|
|
SUB ESP,32 { VAR s: String[31]; }
|
|
|
|
PUSH EAX
|
|
PUSH EDX
|
|
|
|
PUSH dword ptr [ESP+8+32+8] { Str( val : 0, s ); }
|
|
PUSH dword ptr [ESP+8+32+8]
|
|
XOR EAX,EAX
|
|
LEA EDX,[ESP+8+8]
|
|
CALL _StrInt64
|
|
|
|
POP ECX
|
|
POP EAX
|
|
|
|
MOV EDX,ESP { Write( t, s : width );}
|
|
CALL _WriteString
|
|
|
|
ADD ESP,32
|
|
RET 8
|
|
end;
|
|
|
|
procedure _Write0Int64;
|
|
asm
|
|
{ PROCEDURE _Write0Long( VAR t: Text; val: Longint); }
|
|
{ ->EAX Pointer to file record }
|
|
{ EDX Value }
|
|
XOR EDX,EDX
|
|
JMP _WriteInt64
|
|
end;
|
|
|
|
procedure _ReadInt64;
|
|
asm
|
|
// -> EAX Pointer to text record
|
|
// <- EAX:EDX Result
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
SUB ESP,36 // var numbuf: String[32];
|
|
|
|
MOV ESI,EAX
|
|
CALL _SeekEof
|
|
DEC AL
|
|
JZ @@eof
|
|
|
|
MOV EDI,ESP // EDI -> numBuf[0]
|
|
MOV BL,32
|
|
@@loop:
|
|
MOV EAX,ESI
|
|
CALL _ReadChar
|
|
CMP AL,' '
|
|
JBE @@endNum
|
|
STOSB
|
|
DEC BL
|
|
JNZ @@loop
|
|
@@convert:
|
|
MOV byte ptr [EDI],0
|
|
MOV EAX,ESP // EAX -> numBuf
|
|
PUSH EDX // allocate code result
|
|
MOV EDX,ESP // pass pointer to code
|
|
CALL _ValInt64 // convert
|
|
POP ECX // pop code result into EDX
|
|
TEST ECX,ECX
|
|
JZ @@exit
|
|
MOV EAX,106
|
|
CALL SetInOutRes
|
|
|
|
@@exit:
|
|
ADD ESP,36
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
RET
|
|
|
|
@@endNum:
|
|
CMP AH,cEof
|
|
JE @@convert
|
|
DEC [ESI].TTextRec.BufPos
|
|
JMP @@convert
|
|
|
|
@@eof:
|
|
XOR EAX,EAX
|
|
JMP @@exit
|
|
end;
|
|
|
|
function _ValInt64(const s: AnsiString; var code: Integer): Int64;
|
|
var
|
|
i: Integer;
|
|
dig: Integer;
|
|
sign: Boolean;
|
|
empty: Boolean;
|
|
begin
|
|
i := 1;
|
|
dig := 0;
|
|
Result := 0;
|
|
if s = '' then
|
|
begin
|
|
code := i;
|
|
exit;
|
|
end;
|
|
while s[i] = ' ' do
|
|
Inc(i);
|
|
sign := False;
|
|
if s[i] = '-' then
|
|
begin
|
|
sign := True;
|
|
Inc(i);
|
|
end
|
|
else if s[i] = '+' then
|
|
Inc(i);
|
|
empty := True;
|
|
if (s[i] = '$') or (s[i] = '0') and (Upcase(s[i+1]) = 'X') then
|
|
begin
|
|
if s[i] = '0' then
|
|
Inc(i);
|
|
Inc(i);
|
|
while True do
|
|
begin
|
|
case s[i] of
|
|
'0'..'9': dig := Ord(s[i]) - Ord('0');
|
|
'A'..'F': dig := Ord(s[i]) - (Ord('A') - 10);
|
|
'a'..'f': dig := Ord(s[i]) - (Ord('a') - 10);
|
|
else
|
|
break;
|
|
end;
|
|
if (Result < 0) or (Result > (High(Int64) div 16)) then
|
|
break;
|
|
Result := Result shl 4 + dig;
|
|
Inc(i);
|
|
empty := False;
|
|
end;
|
|
if sign then
|
|
Result := - Result;
|
|
end
|
|
else
|
|
begin
|
|
while True do
|
|
begin
|
|
case s[i] of
|
|
'0'..'9': dig := Ord(s[i]) - Ord('0');
|
|
else
|
|
break;
|
|
end;
|
|
if (Result < 0) or (Result > (High(Int64) div 10)) then
|
|
break;
|
|
Result := Result*10 + dig;
|
|
Inc(i);
|
|
empty := False;
|
|
end;
|
|
if sign then
|
|
Result := - Result;
|
|
if (Result <> 0) and (sign <> (Result < 0)) then
|
|
Dec(i);
|
|
end;
|
|
if (s[i] <> #0) or empty then
|
|
code := i
|
|
else
|
|
code := 0;
|
|
end;
|
|
|
|
procedure _DynArrayLength;
|
|
asm
|
|
{ FUNCTION _DynArrayLength(const a: array of ...): Longint; }
|
|
{ ->EAX Pointer to array or nil }
|
|
{ <-EAX High bound of array + 1 or 0 }
|
|
TEST EAX,EAX
|
|
JZ @@skip
|
|
MOV EAX,[EAX-4]
|
|
@@skip:
|
|
end;
|
|
|
|
procedure _DynArrayHigh;
|
|
asm
|
|
{ FUNCTION _DynArrayHigh(const a: array of ...): Longint; }
|
|
{ ->EAX Pointer to array or nil }
|
|
{ <-EAX High bound of array or -1 }
|
|
CALL _DynArrayLength
|
|
DEC EAX
|
|
end;
|
|
|
|
procedure CopyArray(dest, source, typeInfo: Pointer; cnt: Integer);
|
|
asm
|
|
PUSH dword ptr [EBP+8]
|
|
CALL _CopyArray
|
|
end;
|
|
|
|
procedure FinalizeArray(p, typeInfo: Pointer; cnt: Integer);
|
|
asm
|
|
JMP _FinalizeArray
|
|
end;
|
|
|
|
procedure DynArrayClear(var a: Pointer; typeInfo: Pointer);
|
|
asm
|
|
CALL _DynArrayClear
|
|
end;
|
|
|
|
procedure DynArraySetLength(var a: Pointer; typeInfo: Pointer; dimCnt: Longint; lengthVec: PLongint);
|
|
var
|
|
i: Integer;
|
|
newLength, oldLength, minLength: Longint;
|
|
elSize: Longint;
|
|
neededSize: Longint;
|
|
p, pp: Pointer;
|
|
begin
|
|
p := a;
|
|
|
|
// Fetch the new length of the array in this dimension, and the old length
|
|
newLength := PLongint(lengthVec)^;
|
|
if newLength <= 0 then
|
|
begin
|
|
if newLength < 0 then
|
|
Error(reRangeError);
|
|
DynArrayClear(a, typeInfo);
|
|
exit;
|
|
end;
|
|
|
|
oldLength := 0;
|
|
if p <> nil then
|
|
begin
|
|
Dec(PLongint(p));
|
|
oldLength := PLongint(p)^;
|
|
Dec(PLongint(p));
|
|
end;
|
|
|
|
// Calculate the needed size of the heap object
|
|
Inc(PChar(typeInfo), Length(PDynArrayTypeInfo(typeInfo).name));
|
|
elSize := PDynArrayTypeInfo(typeInfo).elSize;
|
|
if PDynArrayTypeInfo(typeInfo).elType <> nil then
|
|
typeInfo := PDynArrayTypeInfo(typeInfo).elType^
|
|
else
|
|
typeInfo := nil;
|
|
neededSize := newLength*elSize;
|
|
if neededSize div newLength <> elSize then
|
|
Error(reRangeError);
|
|
Inc(neededSize, Sizeof(Longint)*2);
|
|
|
|
// If the heap object isn't shared (ref count = 1), just resize it. Otherwise, we make a copy
|
|
if (p = nil) or (PLongint(p)^ = 1) then
|
|
begin
|
|
pp := p;
|
|
if (newLength < oldLength) and (typeInfo <> nil) then
|
|
FinalizeArray(PChar(p) + Sizeof(Longint)*2 + newLength*elSize, typeInfo, oldLength - newLength);
|
|
ReallocMem(pp, neededSize);
|
|
p := pp;
|
|
end
|
|
else
|
|
begin
|
|
Dec(PLongint(p)^);
|
|
GetMem(p, neededSize);
|
|
minLength := oldLength;
|
|
if minLength > newLength then
|
|
minLength := newLength;
|
|
if typeInfo <> nil then
|
|
begin
|
|
FillChar((PChar(p) + Sizeof(Longint)*2)^, minLength*elSize, 0);
|
|
CopyArray(PChar(p) + Sizeof(Longint)*2, a, typeInfo, minLength)
|
|
end
|
|
else
|
|
Move(PChar(a)^, (PChar(p) + Sizeof(Longint)*2)^, minLength*elSize);
|
|
end;
|
|
|
|
// The heap object will now have a ref count of 1 and the new length
|
|
PLongint(p)^ := 1;
|
|
Inc(PLongint(p));
|
|
PLongint(p)^ := newLength;
|
|
Inc(PLongint(p));
|
|
|
|
// Set the new memory to all zero bits
|
|
FillChar((PChar(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);
|
|
|
|
// Take care of the inner dimensions, if any
|
|
if dimCnt > 1 then
|
|
begin
|
|
Inc(lengthVec);
|
|
Dec(dimCnt);
|
|
for i := 0 to newLength-1 do
|
|
DynArraySetLength(PPointerArray(p)[i], typeInfo, dimCnt, lengthVec);
|
|
end;
|
|
a := p;
|
|
end;
|
|
|
|
procedure _DynArraySetLength;
|
|
asm
|
|
{ PROCEDURE _DynArraySetLength(var a: dynarray; typeInfo: PDynArrayTypeInfo; dimCnt: Longint; lengthVec: ^Longint) }
|
|
{ ->EAX Pointer to dynamic array (= pointer to pointer to heap object) }
|
|
{ EDX Pointer to type info for the dynamic array }
|
|
{ ECX number of dimensions }
|
|
{ [ESP+4] dimensions }
|
|
PUSH ESP
|
|
ADD dword ptr [ESP],4
|
|
CALL DynArraySetLength
|
|
end;
|
|
|
|
procedure _DynArrayCopy(a: Pointer; typeInfo: Pointer; var Result: Pointer);
|
|
begin
|
|
if a <> nil then
|
|
_DynArrayCopyRange(a, typeInfo, 0, PLongint(PChar(a)-4)^, Result)
|
|
else
|
|
_DynArrayClear(Result, typeInfo);
|
|
end;
|
|
|
|
procedure _DynArrayCopyRange(a: Pointer; typeInfo: Pointer; index, count : Integer; var Result: Pointer);
|
|
var
|
|
arrayLength: Integer;
|
|
elSize: Integer;
|
|
typeInf: PDynArrayTypeInfo;
|
|
p: Pointer;
|
|
begin
|
|
p := nil;
|
|
if a <> nil then
|
|
begin
|
|
typeInf := typeInfo;
|
|
|
|
// Limit index and count to values within the array
|
|
if index < 0 then
|
|
begin
|
|
Inc(count, index);
|
|
index := 0;
|
|
end;
|
|
arrayLength := PLongint(PChar(a)-4)^;
|
|
if index > arrayLength then
|
|
index := arrayLength;
|
|
if count > arrayLength - index then
|
|
count := arrayLength - index;
|
|
if count < 0 then
|
|
count := 0;
|
|
|
|
if count > 0 then
|
|
begin
|
|
// Figure out the size and type descriptor of the element type
|
|
Inc(PChar(typeInf), Length(typeInf.name));
|
|
elSize := typeInf.elSize;
|
|
if typeInf.elType <> nil then
|
|
typeInf := typeInf.elType^
|
|
else
|
|
typeInf := nil;
|
|
|
|
// Allocate the amount of memory needed
|
|
GetMem(p, count*elSize + Sizeof(Longint)*2);
|
|
|
|
// The reference count of the new array is 1, the length is count
|
|
PLongint(p)^ := 1;
|
|
Inc(PLongint(p));
|
|
PLongint(p)^ := count;
|
|
Inc(PLongint(p));
|
|
Inc(PChar(a), index*elSize);
|
|
|
|
// If the element type needs destruction, we must copy each element,
|
|
// otherwise we can just copy the bits
|
|
if count > 0 then
|
|
begin
|
|
if typeInf <> nil then
|
|
begin
|
|
FillChar(p^, count*elSize, 0);
|
|
CopyArray(p, a, typeInf, count)
|
|
end
|
|
else
|
|
Move(a^, p^, count*elSize);
|
|
end;
|
|
end;
|
|
end;
|
|
DynArrayClear(Result, typeInfo);
|
|
Result := p;
|
|
end;
|
|
|
|
procedure _DynArrayClear;
|
|
asm
|
|
{ ->EAX Pointer to dynamic array (Pointer to pointer to heap object }
|
|
{ EDX Pointer to type info }
|
|
|
|
{ Nothing to do if Pointer to heap object is nil }
|
|
MOV ECX,[EAX]
|
|
TEST ECX,ECX
|
|
JE @@exit
|
|
|
|
{ Set the variable to be finalized to nil }
|
|
MOV dword ptr [EAX],0
|
|
|
|
{ Decrement ref count. Nothing to do if not zero now. }
|
|
{X LOCK} DEC dword ptr [ECX-8]
|
|
JNE @@exit
|
|
|
|
{ Save the source - we're supposed to return it }
|
|
PUSH EAX
|
|
MOV EAX,ECX
|
|
|
|
{ Fetch the type descriptor of the elements }
|
|
XOR ECX,ECX
|
|
MOV CL,[EDX].TDynArrayTypeInfo.name;
|
|
MOV EDX,[EDX+ECX].TDynArrayTypeInfo.elType;
|
|
|
|
{ If it's non-nil, finalize the elements }
|
|
TEST EDX,EDX
|
|
JE @@noFinalize
|
|
MOV ECX,[EAX-4]
|
|
TEST ECX,ECX
|
|
JE @@noFinalize
|
|
MOV EDX,[EDX]
|
|
CALL _FinalizeArray
|
|
@@noFinalize:
|
|
{ Now deallocate the array }
|
|
SUB EAX,8
|
|
CALL _FreeMem
|
|
POP EAX
|
|
@@exit:
|
|
end;
|
|
|
|
|
|
procedure _DynArrayAsg;
|
|
asm
|
|
{ ->EAX Pointer to destination (pointer to pointer to heap object }
|
|
{ EDX source (pointer to heap object }
|
|
{ ECX Pointer to rtti describing dynamic array }
|
|
|
|
PUSH EBX
|
|
MOV EBX,[EAX]
|
|
|
|
{ Increment ref count of source if non-nil }
|
|
|
|
TEST EDX,EDX
|
|
JE @@skipInc
|
|
{X LOCK} INC dword ptr [EDX-8]
|
|
@@skipInc:
|
|
{ Dec ref count of destination - if it becomes 0, clear dest }
|
|
TEST EBX,EBX
|
|
JE @@skipClear
|
|
{X LOCK} DEC dword ptr[EBX-8]
|
|
JNZ @@skipClear
|
|
PUSH EAX
|
|
PUSH EDX
|
|
MOV EDX,ECX
|
|
INC dword ptr[EBX-8]
|
|
CALL _DynArrayClear
|
|
POP EDX
|
|
POP EAX
|
|
@@skipClear:
|
|
{ Finally store source into destination }
|
|
MOV [EAX],EDX
|
|
|
|
POP EBX
|
|
end;
|
|
|
|
procedure _DynArrayAddRef;
|
|
asm
|
|
{ ->EAX Pointer to heap object }
|
|
TEST EAX,EAX
|
|
JE @@exit
|
|
{X LOCK} INC dword ptr [EAX-8]
|
|
@@exit:
|
|
end;
|
|
|
|
|
|
function DynArrayIndex(const P: Pointer; const Indices: array of Integer; const TypInfo: Pointer): Pointer;
|
|
asm
|
|
{ ->EAX P }
|
|
{ EDX Pointer to Indices }
|
|
{ ECX High bound of Indices }
|
|
{ [EBP+8] TypInfo }
|
|
|
|
PUSH EBX
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBP
|
|
|
|
MOV ESI,EDX
|
|
MOV EDI,[EBP+8]
|
|
MOV EBP,EAX
|
|
|
|
XOR EBX,EBX { for i := 0 to High(Indices) do }
|
|
TEST ECX,ECX
|
|
JGE @@start
|
|
@@loop:
|
|
MOV EBP,[EBP]
|
|
@@start:
|
|
XOR EAX,EAX
|
|
MOV AL,[EDI].TDynArrayTypeInfo.name
|
|
ADD EDI,EAX
|
|
MOV EAX,[ESI+EBX*4] { P := P + Indices[i]*TypInfo.elSize }
|
|
MUL [EDI].TDynArrayTypeInfo.elSize
|
|
MOV EDI,[EDI].TDynArrayTypeInfo.elType
|
|
TEST EDI,EDI
|
|
JE @@skip
|
|
MOV EDI,[EDI]
|
|
@@skip:
|
|
ADD EBP,EAX
|
|
INC EBX
|
|
CMP EBX,ECX
|
|
JLE @@loop
|
|
|
|
@@loopEnd:
|
|
|
|
MOV EAX,EBP
|
|
|
|
POP EBP
|
|
POP EDI
|
|
POP ESI
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
{ Returns the DynArrayTypeInfo of the Element Type of the specified DynArrayTypeInfo }
|
|
function DynArrayElTypeInfo(typeInfo: PDynArrayTypeInfo): PDynArrayTypeInfo;
|
|
begin
|
|
Result := nil;
|
|
if typeInfo <> nil then
|
|
begin
|
|
Inc(PChar(typeInfo), Length(typeInfo.name));
|
|
if typeInfo.elType <> nil then
|
|
Result := typeInfo.elType^;
|
|
end;
|
|
end;
|
|
|
|
{ Returns # of dimemsions of the DynArray described by the specified DynArrayTypeInfo}
|
|
function DynArrayDim(typeInfo: PDynArrayTypeInfo): Integer;
|
|
begin
|
|
Result := 0;
|
|
while (typeInfo <> nil) and (typeInfo.kind = tkDynArray) do
|
|
begin
|
|
Inc(Result);
|
|
typeInfo := DynArrayElTypeInfo(typeInfo);
|
|
end;
|
|
end;
|
|
|
|
{ Returns size of the Dynamic Array}
|
|
function DynArraySize(a: Pointer): Integer;
|
|
asm
|
|
TEST EAX, EAX
|
|
JZ @@exit
|
|
MOV EAX, [EAX-4]
|
|
@@exit:
|
|
end;
|
|
|
|
// Returns whether array is rectangular
|
|
function IsDynArrayRectangular(const DynArray: Pointer; typeInfo: PDynArrayTypeInfo): Boolean;
|
|
var
|
|
Dim, I, J, Size, SubSize: Integer;
|
|
P: Pointer;
|
|
begin
|
|
// Assume we have a rectangular array
|
|
Result := True;
|
|
|
|
P := DynArray;
|
|
Dim := DynArrayDim(typeInfo);
|
|
|
|
{NOTE: Start at 1. Don't need to test the first dimension - it's rectangular by definition}
|
|
for I := 1 to dim-1 do
|
|
begin
|
|
if P <> nil then
|
|
begin
|
|
{ Get size of this dimension }
|
|
Size := DynArraySize(P);
|
|
|
|
{ Get Size of first sub. dimension }
|
|
SubSize := DynArraySize(PPointerArray(P)[0]);
|
|
|
|
{ Walk through every dimension making sure they all have the same size}
|
|
for J := 1 to Size-1 do
|
|
if DynArraySize(PPointerArray(P)[J]) <> SubSize then
|
|
begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
{ Point to next dimension}
|
|
P := PPointerArray(P)[0];
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Returns Bounds of Dynamic array as an array of integer containing the 'high' of each dimension
|
|
function DynArrayBounds(const DynArray: Pointer; typeInfo: PDynArrayTypeInfo): TBoundArray;
|
|
var
|
|
Dim, I: Integer;
|
|
P: Pointer;
|
|
begin
|
|
P := DynArray;
|
|
|
|
Dim := DynArrayDim(typeInfo);
|
|
SetLength(Result, Dim);
|
|
|
|
for I := 0 to dim-1 do
|
|
if P <> nil then
|
|
begin
|
|
Result[I] := DynArraySize(P)-1;
|
|
P := PPointerArray(P)[0]; // Assume rectangular arrays
|
|
end;
|
|
end;
|
|
|
|
{ Decrements to next lower index - Returns True if successful }
|
|
{ Indices: Indices to be decremented }
|
|
{ Bounds : High bounds of each dimension }
|
|
function DecIndices(var Indices: TBoundArray; const Bounds: TBoundArray): Boolean;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
{ Find out if we're done: all at zeroes }
|
|
Result := False;
|
|
for I := Low(Indices) to High(Indices) do
|
|
if Indices[I] <> 0 then
|
|
begin
|
|
Result := True;
|
|
break;
|
|
end;
|
|
if not Result then
|
|
Exit;
|
|
|
|
{ Two arrays must be of same length }
|
|
Assert(Length(Indices) = Length(Bounds));
|
|
|
|
{ Find index of item to tweak }
|
|
for I := High(Indices) downto Low(Bounds) do
|
|
begin
|
|
// If not reach zero, dec and bail out
|
|
if Indices[I] <> 0 then
|
|
begin
|
|
Dec(Indices[I]);
|
|
Exit;
|
|
end
|
|
else
|
|
begin
|
|
J := I;
|
|
while Indices[J] = 0 do
|
|
begin
|
|
// Restore high bound when we've reached zero on a particular dimension
|
|
Indices[J] := Bounds[J];
|
|
// Move to higher dimension
|
|
Dec(J);
|
|
Assert(J >= 0);
|
|
end;
|
|
Dec(Indices[J]);
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ Package/Module registration/unregistration }
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
const
|
|
LOCALE_SABBREVLANGNAME = $00000003; { abbreviated language name }
|
|
LOAD_LIBRARY_AS_DATAFILE = 2;
|
|
HKEY_CURRENT_USER = $80000001;
|
|
KEY_ALL_ACCESS = $000F003F;
|
|
KEY_READ = $000F0019;
|
|
|
|
OldLocaleOverrideKey = 'Software\Borland\Delphi\Locales'; // do not localize
|
|
NewLocaleOverrideKey = 'Software\Borland\Locales'; // do not localize
|
|
{$ENDIF}
|
|
|
|
function FindModule(Instance: LongWord): PLibModule;
|
|
begin
|
|
Result := LibModuleList;
|
|
while Result <> nil do
|
|
begin
|
|
if (Instance = Result.Instance) or
|
|
(Instance = Result.CodeInstance) or
|
|
(Instance = Result.DataInstance) or
|
|
(Instance = Result.ResInstance) then
|
|
Exit;
|
|
Result := Result.Next;
|
|
end;
|
|
end;
|
|
|
|
function FindHInstance(Address: Pointer): LongWord;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
MemInfo: TMemInfo;
|
|
begin
|
|
VirtualQuery(Address, MemInfo, SizeOf(MemInfo));
|
|
if MemInfo.State = $1000{MEM_COMMIT} then
|
|
Result := LongWord(MemInfo.AllocationBase)
|
|
else
|
|
Result := 0;
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
var
|
|
Info: TDLInfo;
|
|
begin
|
|
if (dladdr(Address, Info) = 0) or (Info.BaseAddress = ExeBaseAddress) then
|
|
Info.Filename := nil; // if it's not in a library, assume the exe
|
|
Result := LongWord(dlopen(Info.Filename, RTLD_LAZY));
|
|
if Result <> 0 then
|
|
dlclose(Result);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function FindClassHInstance(ClassType: TClass): LongWord;
|
|
begin
|
|
Result := FindHInstance(Pointer(ClassType));
|
|
end;
|
|
|
|
{$IFDEF LINUX}
|
|
function GetModuleFileName(Module: HMODULE; Buffer: PChar; BufLen: Integer): Integer;
|
|
var
|
|
Addr: Pointer;
|
|
Info: TDLInfo;
|
|
FoundInModule: HMODULE;
|
|
begin
|
|
Result := 0;
|
|
if (Module = MainInstance) or (Module = 0) then
|
|
begin
|
|
// First, try the dlsym approach.
|
|
// dladdr fails to return the name of the main executable
|
|
// in glibc prior to 2.1.91
|
|
|
|
{ Look for a dynamic symbol exported from this program.
|
|
_DYNAMIC is not required in a main program file.
|
|
If the main program is compiled with Delphi, it will always
|
|
have a resource section, named @Sysinit@ResSym.
|
|
If the main program is not compiled with Delphi, dlsym
|
|
will search the global name space, potentially returning
|
|
the address of a symbol in some other shared object library
|
|
loaded by the program. To guard against that, we check
|
|
that the address of the symbol found is within the
|
|
main program address range. }
|
|
|
|
dlerror; // clear error state; dlsym doesn't
|
|
Addr := dlsym(Module, '@Sysinit@ResSym');
|
|
if (Addr <> nil) and (dlerror = nil)
|
|
and (dladdr(Addr, Info) <> 0)
|
|
and (Info.FileName <> nil)
|
|
and (Info.BaseAddress = ExeBaseAddress) then
|
|
begin
|
|
Result := _strlen(Info.FileName);
|
|
if Result >= BufLen then Result := BufLen-1;
|
|
Move(Info.FileName^, Buffer^, Result);
|
|
Buffer[Result] := #0;
|
|
Exit;
|
|
end;
|
|
|
|
// Try inspecting the /proc/ virtual file system
|
|
// to find the program filename in the process info
|
|
Result := _readlink('/proc/self/exe', Buffer, BufLen);
|
|
if Result <> -1 then
|
|
begin
|
|
if Result >= BufLen then Result := BufLen-1;
|
|
Buffer[Result] := #0;
|
|
end;
|
|
{$IFDEF AllowParamStrModuleName}
|
|
{ Using ParamStr(0) to obtain a module name presents a potential
|
|
security hole. Resource modules are loaded based upon the filename
|
|
of a given module. We use dlopen() to load resource modules, which
|
|
means the .init code of the resource module will be executed.
|
|
Normally, resource modules contain no code at all - they're just
|
|
carriers of resource data.
|
|
An unpriviledged user program could launch our trusted,
|
|
priviledged program with a bogus parameter list, tricking us
|
|
into loading a module that contains malicious code in its
|
|
.init section.
|
|
Without this ParamStr(0) section, GetModuleFilename cannot be
|
|
misdirected by unpriviledged code (unless the system program loader
|
|
or the /proc file system or system root directory has been compromised).
|
|
Resource modules are always loaded from the same directory as the
|
|
given module. Trusted code (programs, packages, and libraries)
|
|
should reside in directories that unpriviledged code cannot alter.
|
|
|
|
If you need GetModuleFilename to have a chance of working on systems
|
|
where glibc < 2.1.91 and /proc is not available, and your
|
|
program will not run as a priviledged user (or you don't care),
|
|
you can define AllowParamStrModuleNames and rebuild the System unit
|
|
and baseCLX package. Note that even with ParamStr(0) support
|
|
enabled, GetModuleFilename can still fail to find the name of
|
|
a module. C'est la Unix. }
|
|
|
|
if Result = -1 then // couldn't access the /proc filesystem
|
|
begin // return less accurate ParamStr(0)
|
|
|
|
{ ParamStr(0) returns the name of the link used
|
|
to launch the app, not the name of the app itself.
|
|
Also, if this app was launched by some other program,
|
|
there is no guarantee that the launching program has set
|
|
up our environment at all. (example: Apache CGI) }
|
|
|
|
if (ArgValues = nil) or (ArgValues^ = nil) or
|
|
(PCharArray(ArgValues^)[0] = nil) then
|
|
begin
|
|
Result := 0;
|
|
Exit;
|
|
end;
|
|
Result := _strlen(PCharArray(ArgValues^)[0]);
|
|
if Result >= BufLen then Result := BufLen-1;
|
|
Move(PCharArray(ArgValues^)[0]^, Buffer^, Result);
|
|
Buffer[Result] := #0;
|
|
end;
|
|
{$ENDIF}
|
|
end
|
|
else
|
|
begin
|
|
{ For shared object libraries, we can rely on the dlsym technique.
|
|
Look for a dynamic symbol in the requested module.
|
|
Don't assume the module was compiled with Delphi.
|
|
We look for a dynamic symbol with the name _DYNAMIC. This
|
|
exists in all ELF shared object libraries that export
|
|
or import symbols; If someone has a shared object library that
|
|
contains no imports or exports of any kind, this will probably fail.
|
|
If dlsym can't find the requested symbol in the given module, it
|
|
will search the global namespace and could return the address
|
|
of a symbol from some other module that happens to be loaded
|
|
into this process. That would be bad, so we double check
|
|
that the module handle of the symbol found matches the
|
|
module handle we asked about.}
|
|
|
|
dlerror; // clear error state; dlsym doesn't
|
|
Addr := dlsym(Module, '_DYNAMIC');
|
|
if (Addr <> nil) and (dlerror = nil)
|
|
and (dladdr(Addr, Info) <> 0) then
|
|
begin
|
|
if Info.BaseAddress = ExeBaseAddress then
|
|
Info.FileName := nil;
|
|
FoundInModule := HMODULE(dlopen(Info.FileName, RTLD_LAZY));
|
|
if FoundInModule <> 0 then
|
|
dlclose(FoundInModule);
|
|
if Module = FoundInModule then
|
|
begin
|
|
Result := _strlen(Info.FileName);
|
|
if Result >= BufLen then Result := BufLen-1;
|
|
Move(Info.FileName^, Buffer^, Result);
|
|
Buffer[Result] := #0;
|
|
end;
|
|
end;
|
|
end;
|
|
if Result < 0 then Result := 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function DelayLoadResourceModule(Module: PLibModule): LongWord;
|
|
var
|
|
FileName: array[0..MAX_PATH] of Char;
|
|
begin
|
|
if Module.ResInstance = 0 then
|
|
begin
|
|
GetModuleFileName(Module.Instance, FileName, SizeOf(FileName));
|
|
Module.ResInstance := LoadResourceModule(FileName);
|
|
if Module.ResInstance = 0 then
|
|
Module.ResInstance := Module.Instance;
|
|
end;
|
|
Result := Module.ResInstance;
|
|
end;
|
|
|
|
function FindResourceHInstance(Instance: LongWord): LongWord;
|
|
var
|
|
CurModule: PLibModule;
|
|
begin
|
|
CurModule := LibModuleList;
|
|
while CurModule <> nil do
|
|
begin
|
|
if (Instance = CurModule.Instance) or
|
|
(Instance = CurModule.CodeInstance) or
|
|
(Instance = CurModule.DataInstance) then
|
|
begin
|
|
Result := DelayLoadResourceModule(CurModule);
|
|
Exit;
|
|
end;
|
|
CurModule := CurModule.Next;
|
|
end;
|
|
Result := Instance;
|
|
end;
|
|
|
|
function LoadResourceModule(ModuleName: PChar; CheckOwner: Boolean): LongWord;
|
|
{$IFDEF LINUX}
|
|
var
|
|
FileName: array [0..MAX_PATH] of Char;
|
|
LangCode: PChar; // Language and country code. Example: en_US
|
|
P: PChar;
|
|
ModuleNameLen, FileNameLen, i: Integer;
|
|
st1, st2: TStatStruct;
|
|
begin
|
|
LangCode := __getenv('LANG');
|
|
Result := 0;
|
|
if (LangCode = nil) or (LangCode^ = #0) then Exit;
|
|
|
|
// look for modulename.en_US (ignoring codeset and modifier suffixes)
|
|
P := LangCode;
|
|
while P^ in ['a'..'z', 'A'..'Z', '_'] do
|
|
Inc(P);
|
|
if P = LangCode then Exit;
|
|
|
|
if CheckOwner and (__xstat(STAT_VER_LINUX, ModuleName, st1) = -1) then
|
|
Exit;
|
|
|
|
ModuleNameLen := _strlen(ModuleName);
|
|
if (ModuleNameLen + P - LangCode) >= MAX_PATH then Exit;
|
|
Move(ModuleName[0], Filename[0], ModuleNameLen);
|
|
Filename[ModuleNameLen] := '.';
|
|
Move(LangCode[0], Filename[ModuleNameLen + 1], P - LangCode);
|
|
FileNameLen := ModuleNameLen + 1 + (P - LangCode);
|
|
Filename[FileNameLen] := #0;
|
|
|
|
{ Security check: make sure the user id (owner) and group id of
|
|
the base module matches the user id and group id of the resource
|
|
module we're considering loading. This is to prevent loading
|
|
of malicious code dropped into the base module's directory by
|
|
a hostile user. The app and all its resource modules must
|
|
have the same owner and group. To disable this security check,
|
|
call this function with CheckOwner set to False. }
|
|
|
|
if (not CheckOwner) or
|
|
((__xstat(STAT_VER_LINUX, FileName, st2) <> -1)
|
|
and (st1.st_uid = st2.st_uid)
|
|
and (st1.st_gid = st2.st_gid)) then
|
|
begin
|
|
Result := dlopen(Filename, RTLD_LAZY);
|
|
if Result <> 0 then Exit;
|
|
end;
|
|
|
|
// look for modulename.en (ignoring country code and suffixes)
|
|
i := ModuleNameLen + 1;
|
|
while (i <= FileNameLen) and (Filename[i] in ['a'..'z', 'A'..'Z']) do
|
|
Inc(i);
|
|
if (i = ModuleNameLen + 1) or (i > FileNameLen) then Exit;
|
|
FileName[i] := #0;
|
|
|
|
{ Security check. See notes above. }
|
|
if (not CheckOwner) or
|
|
((__xstat(STAT_VER_LINUX, FileName, st2) <> -1)
|
|
and (st1.st_uid = st2.st_uid)
|
|
and (st1.st_gid = st2.st_gid)) then
|
|
begin
|
|
Result := dlopen(FileName, RTLD_LAZY);
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
FileName: array[0..MAX_PATH] of Char;
|
|
Key: LongWord;
|
|
LocaleName, LocaleOverride: array[0..4] of Char;
|
|
Size: Integer;
|
|
P: PChar;
|
|
|
|
function FindBS(Current: PChar): PChar;
|
|
begin
|
|
Result := Current;
|
|
while (Result^ <> #0) and (Result^ <> '\') do
|
|
Result := CharNext(Result);
|
|
end;
|
|
|
|
function ToLongPath(AFileName: PChar; BufSize: Integer): PChar;
|
|
var
|
|
CurrBS, NextBS: PChar;
|
|
Handle, L: Integer;
|
|
FindData: TWin32FindData;
|
|
Buffer: array[0..MAX_PATH] of Char;
|
|
GetLongPathName: function (ShortPathName: PChar; LongPathName: PChar;
|
|
cchBuffer: Integer): Integer stdcall;
|
|
begin
|
|
Result := AFileName;
|
|
Handle := GetModuleHandle(kernel);
|
|
if Handle <> 0 then
|
|
begin
|
|
@GetLongPathName := GetProcAddress(Handle, 'GetLongPathNameA');
|
|
if Assigned(GetLongPathName) and
|
|
(GetLongPathName(AFileName, Buffer, SizeOf(Buffer)) <> 0) then
|
|
begin
|
|
lstrcpyn(AFileName, Buffer, BufSize);
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
if AFileName[0] = '\' then
|
|
begin
|
|
if AFileName[1] <> '\' then Exit;
|
|
CurrBS := FindBS(AFileName + 2); // skip server name
|
|
if CurrBS^ = #0 then Exit;
|
|
CurrBS := FindBS(CurrBS + 1); // skip share name
|
|
if CurrBS^ = #0 then Exit;
|
|
end else
|
|
CurrBS := AFileName + 2; // skip drive name
|
|
|
|
L := CurrBS - AFileName;
|
|
lstrcpyn(Buffer, AFileName, L + 1);
|
|
while CurrBS^ <> #0 do
|
|
begin
|
|
NextBS := FindBS(CurrBS + 1);
|
|
if L + (NextBS - CurrBS) + 1 > SizeOf(Buffer) then Exit;
|
|
lstrcpyn(Buffer + L, CurrBS, (NextBS - CurrBS) + 1);
|
|
|
|
Handle := FindFirstFile(Buffer, FindData);
|
|
if (Handle = -1) then Exit;
|
|
FindClose(Handle);
|
|
|
|
if L + 1 + _strlen(FindData.cFileName) + 1 > SizeOf(Buffer) then Exit;
|
|
Buffer[L] := '\';
|
|
lstrcpyn(Buffer + L + 1, FindData.cFileName, Sizeof(Buffer) - L - 1);
|
|
Inc(L, _strlen(FindData.cFileName) + 1);
|
|
CurrBS := NextBS;
|
|
end;
|
|
lstrcpyn(AFileName, Buffer, BufSize);
|
|
end;
|
|
begin
|
|
GetModuleFileName(0, FileName, SizeOf(FileName)); // Get host application name
|
|
LocaleOverride[0] := #0;
|
|
if (RegOpenKeyEx(HKEY_CURRENT_USER, NewLocaleOverrideKey, 0, KEY_READ, Key) = 0) or
|
|
(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NewLocaleOverrideKey, 0, KEY_READ, Key) = 0) or
|
|
(RegOpenKeyEx(HKEY_CURRENT_USER, OldLocaleOverrideKey, 0, KEY_READ, Key) = 0) then
|
|
try
|
|
Size := sizeof(LocaleOverride);
|
|
ToLongPath(FileName, sizeof(FileName));
|
|
if RegQueryValueEx(Key, FileName, nil, nil, LocaleOverride, @Size) <> 0 then
|
|
if RegQueryValueEx(Key, '', nil, nil, LocaleOverride, @Size) <> 0 then
|
|
LocaleOverride[0] := #0;
|
|
LocaleOverride[sizeof(LocaleOverride)-1] := #0;
|
|
finally
|
|
RegCloseKey(Key);
|
|
end;
|
|
lstrcpyn(FileName, ModuleName, sizeof(FileName));
|
|
GetLocaleInfo(GetThreadLocale, LOCALE_SABBREVLANGNAME, LocaleName, sizeof(LocaleName));
|
|
Result := 0;
|
|
if (FileName[0] <> #0) and ((LocaleName[0] <> #0) or (LocaleOverride[0] <> #0)) then
|
|
begin
|
|
P := PChar(@FileName) + _strlen(FileName);
|
|
while (P^ <> '.') and (P <> @FileName) do Dec(P);
|
|
if P <> @FileName then
|
|
begin
|
|
Inc(P);
|
|
// First look for a locale registry override
|
|
if LocaleOverride[0] <> #0 then
|
|
begin
|
|
lstrcpyn(P, LocaleOverride, sizeof(FileName) - (P - FileName));
|
|
Result := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
|
|
end;
|
|
if (Result = 0) and (LocaleName[0] <> #0) then
|
|
begin
|
|
// Then look for a potential language/country translation
|
|
lstrcpyn(P, LocaleName, sizeof(FileName) - (P - FileName));
|
|
Result := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
|
|
if Result = 0 then
|
|
begin
|
|
// Finally look for a language only translation
|
|
LocaleName[2] := #0;
|
|
lstrcpyn(P, LocaleName, sizeof(FileName) - (P - FileName));
|
|
Result := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure EnumModules(Func: TEnumModuleFunc; Data: Pointer); assembler;
|
|
begin
|
|
EnumModules(TEnumModuleFuncLW(Func), Data);
|
|
end;
|
|
|
|
procedure EnumResourceModules(Func: TEnumModuleFunc; Data: Pointer);
|
|
begin
|
|
EnumResourceModules(TEnumModuleFuncLW(Func), Data);
|
|
end;
|
|
|
|
procedure EnumModules(Func: TEnumModuleFuncLW; Data: Pointer);
|
|
var
|
|
CurModule: PLibModule;
|
|
begin
|
|
CurModule := LibModuleList;
|
|
while CurModule <> nil do
|
|
begin
|
|
if not Func(CurModule.Instance, Data) then Exit;
|
|
CurModule := CurModule.Next;
|
|
end;
|
|
end;
|
|
|
|
procedure EnumResourceModules(Func: TEnumModuleFuncLW; Data: Pointer);
|
|
var
|
|
CurModule: PLibModule;
|
|
begin
|
|
CurModule := LibModuleList;
|
|
while CurModule <> nil do
|
|
begin
|
|
if not Func(DelayLoadResourceModule(CurModule), Data) then Exit;
|
|
CurModule := CurModule.Next;
|
|
end;
|
|
end;
|
|
|
|
procedure AddModuleUnloadProc(Proc: TModuleUnloadProc);
|
|
begin
|
|
AddModuleUnloadProc(TModuleUnloadProcLW(Proc));
|
|
end;
|
|
|
|
procedure RemoveModuleUnloadProc(Proc: TModuleUnloadProc);
|
|
begin
|
|
RemoveModuleUnloadProc(TModuleUnloadProcLW(Proc));
|
|
end;
|
|
|
|
procedure AddModuleUnloadProc(Proc: TModuleUnloadProcLW);
|
|
var
|
|
P: PModuleUnloadRec;
|
|
begin
|
|
New(P);
|
|
P.Next := ModuleUnloadList;
|
|
@P.Proc := @Proc;
|
|
ModuleUnloadList := P;
|
|
end;
|
|
|
|
procedure RemoveModuleUnloadProc(Proc: TModuleUnloadProcLW);
|
|
var
|
|
P, C: PModuleUnloadRec;
|
|
begin
|
|
P := ModuleUnloadList;
|
|
if (P <> nil) and (@P.Proc = @Proc) then
|
|
begin
|
|
ModuleUnloadList := ModuleUnloadList.Next;
|
|
Dispose(P);
|
|
end else
|
|
begin
|
|
C := P;
|
|
while C <> nil do
|
|
begin
|
|
if (C.Next <> nil) and (@C.Next.Proc = @Proc) then
|
|
begin
|
|
P := C.Next;
|
|
C.Next := C.Next.Next;
|
|
Dispose(P);
|
|
Break;
|
|
end;
|
|
C := C.Next;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure NotifyModuleUnload(HInstance: LongWord);
|
|
var
|
|
P: PModuleUnloadRec;
|
|
begin
|
|
P := ModuleUnloadList;
|
|
while P <> nil do
|
|
begin
|
|
try
|
|
P.Proc(HInstance);
|
|
except
|
|
// Make sure it doesn't stop notifications
|
|
end;
|
|
P := P.Next;
|
|
end;
|
|
{$IFDEF LINUX}
|
|
InvalidateModuleCache;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure RegisterModule(LibModule: PLibModule);
|
|
begin
|
|
LibModule.Next := LibModuleList;
|
|
LibModuleList := LibModule;
|
|
end;
|
|
|
|
{X- procedure UnregisterModule(LibModule: PLibModule); -renamed }
|
|
procedure UnRegisterModuleSafely( LibModule: PLibModule );
|
|
var
|
|
CurModule: PLibModule;
|
|
begin
|
|
try
|
|
NotifyModuleUnload(LibModule.Instance);
|
|
finally
|
|
if LibModule = LibModuleList then
|
|
LibModuleList := LibModule.Next
|
|
else
|
|
begin
|
|
CurModule := LibModuleList;
|
|
while CurModule <> nil do
|
|
begin
|
|
if CurModule.Next = LibModule then
|
|
begin
|
|
CurModule.Next := LibModule.Next;
|
|
Break;
|
|
end;
|
|
CurModule := CurModule.Next;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{X+} // "Light" version of UnRegisterModule - without using of try-except
|
|
procedure UnRegisterModuleLight( LibModule: PLibModule );
|
|
var
|
|
P: PModuleUnloadRec;
|
|
begin
|
|
P := ModuleUnloadList;
|
|
while P <> nil do
|
|
begin
|
|
P.Proc(LibModule.Instance);
|
|
P := P.Next;
|
|
end;
|
|
end;
|
|
{X-}
|
|
|
|
function _IntfClear(var Dest: IInterface): Pointer;
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
P: Pointer;
|
|
begin
|
|
Result := @Dest;
|
|
if Dest <> nil then
|
|
begin
|
|
P := Pointer(Dest);
|
|
Pointer(Dest) := nil;
|
|
IInterface(P)._Release;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
MOV EDX,[EAX]
|
|
TEST EDX,EDX
|
|
JE @@1
|
|
MOV DWORD PTR [EAX],0
|
|
PUSH EAX
|
|
PUSH EDX
|
|
MOV EAX,[EDX]
|
|
CALL DWORD PTR [EAX] + VMTOFFSET IInterface._Release
|
|
POP EAX
|
|
@@1:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _IntfCopy(var Dest: IInterface; const Source: IInterface);
|
|
{$IFDEF PUREPASCAL}
|
|
var
|
|
P: Pointer;
|
|
begin
|
|
P := Pointer(Dest);
|
|
if Source <> nil then
|
|
Source._AddRef;
|
|
Pointer(Dest) := Pointer(Source);
|
|
if P <> nil then
|
|
IInterface(P)._Release;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
{
|
|
The most common case is the single assignment of a non-nil interface
|
|
to a nil interface. So we streamline that case here. After this,
|
|
we give essentially equal weight to other outcomes.
|
|
|
|
The semantics are: The source intf must be addrefed *before* it
|
|
is assigned to the destination. The old intf must be released
|
|
after the new intf is addrefed to support self assignment (I := I).
|
|
Either intf can be nil. The first requirement is really to make an
|
|
error case function a little better, and to improve the behaviour
|
|
of multithreaded applications - if the addref throws an exception,
|
|
you don't want the interface to have been assigned here, and if the
|
|
assignment is made to a global and another thread references it,
|
|
again you don't want the intf to be available until the reference
|
|
count is bumped.
|
|
}
|
|
TEST EDX,EDX // is source nil?
|
|
JE @@NilSource
|
|
PUSH EDX // save source
|
|
PUSH EAX // save dest
|
|
MOV EAX,[EDX] // get source vmt
|
|
PUSH EDX // source as arg
|
|
CALL DWORD PTR [EAX] + VMTOFFSET IInterface._AddRef
|
|
POP EAX // retrieve dest
|
|
MOV ECX, [EAX] // get current value
|
|
POP [EAX] // set dest in place
|
|
TEST ECX, ECX // is current value nil?
|
|
JNE @@ReleaseDest // no, release it
|
|
RET // most common case, we return here
|
|
@@ReleaseDest:
|
|
MOV EAX,[ECX] // get current value vmt
|
|
PUSH ECX // current value as arg
|
|
CALL DWORD PTR [EAX] + VMTOFFSET IInterface._Release
|
|
RET
|
|
|
|
{ Now we're into the less common cases. }
|
|
@@NilSource:
|
|
MOV ECX, [EAX] // get current value
|
|
TEST ECX, ECX // is it nil?
|
|
MOV [EAX], EDX // store in dest (which is nil)
|
|
JE @@Done
|
|
MOV EAX, [ECX] // get current vmt
|
|
PUSH ECX // current value as arg
|
|
CALL [EAX].vmtRelease.Pointer
|
|
@@Done:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _IntfCast(var Dest: IInterface; const Source: IInterface; const IID: TGUID);
|
|
{$IFDEF PUREPASCAL}
|
|
// PIC: EBX must be correct before calling QueryInterface
|
|
begin
|
|
if Source = nil then
|
|
Dest := nil
|
|
else if Source.QueryInterface(IID, Dest) <> 0 then
|
|
Error(reIntfCastError);
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
TEST EDX,EDX
|
|
JE _IntfClear
|
|
PUSH EAX
|
|
PUSH ECX
|
|
PUSH EDX
|
|
MOV ECX,[EAX]
|
|
TEST ECX,ECX
|
|
JE @@1
|
|
PUSH ECX
|
|
MOV EAX,[ECX]
|
|
CALL DWORD PTR [EAX] + VMTOFFSET IInterface._Release
|
|
MOV EDX,[ESP]
|
|
@@1: MOV EAX,[EDX]
|
|
CALL DWORD PTR [EAX] + VMTOFFSET IInterface.QueryInterface
|
|
TEST EAX,EAX
|
|
JE @@2
|
|
MOV AL,reIntfCastError
|
|
JMP Error
|
|
@@2:
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure _IntfAddRef(const Dest: IInterface);
|
|
begin
|
|
if Dest <> nil then Dest._AddRef;
|
|
end;
|
|
|
|
procedure TInterfacedObject.AfterConstruction;
|
|
begin
|
|
// Release the constructor's implicit refcount
|
|
InterlockedDecrement(FRefCount);
|
|
end;
|
|
|
|
procedure TInterfacedObject.BeforeDestruction;
|
|
begin
|
|
if RefCount <> 0 then
|
|
Error(reInvalidPtr);
|
|
end;
|
|
|
|
// Set an implicit refcount so that refcounting
|
|
// during construction won't destroy the object.
|
|
class function TInterfacedObject.NewInstance: TObject;
|
|
begin
|
|
Result := inherited NewInstance;
|
|
TInterfacedObject(Result).FRefCount := 1;
|
|
end;
|
|
|
|
function TInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
|
|
begin
|
|
if GetInterface(IID, Obj) then
|
|
Result := 0
|
|
else
|
|
Result := E_NOINTERFACE;
|
|
end;
|
|
|
|
function TInterfacedObject._AddRef: Integer;
|
|
begin
|
|
Result := InterlockedIncrement(FRefCount);
|
|
end;
|
|
|
|
function TInterfacedObject._Release: Integer;
|
|
begin
|
|
Result := InterlockedDecrement(FRefCount);
|
|
if Result = 0 then
|
|
Destroy;
|
|
end;
|
|
|
|
{ TAggregatedObject }
|
|
|
|
constructor TAggregatedObject.Create(const Controller: IInterface);
|
|
begin
|
|
// weak reference to controller - don't keep it alive
|
|
FController := Pointer(Controller);
|
|
end;
|
|
|
|
function TAggregatedObject.GetController: IInterface;
|
|
begin
|
|
Result := IInterface(FController);
|
|
end;
|
|
|
|
function TAggregatedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
|
|
begin
|
|
Result := IInterface(FController).QueryInterface(IID, Obj);
|
|
end;
|
|
|
|
function TAggregatedObject._AddRef: Integer;
|
|
begin
|
|
Result := IInterface(FController)._AddRef;
|
|
end;
|
|
|
|
function TAggregatedObject._Release: Integer; stdcall;
|
|
begin
|
|
Result := IInterface(FController)._Release;
|
|
end;
|
|
|
|
{ TContainedObject }
|
|
|
|
function TContainedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
|
|
begin
|
|
if GetInterface(IID, Obj) then
|
|
Result := S_OK
|
|
else
|
|
Result := E_NOINTERFACE;
|
|
end;
|
|
|
|
|
|
function _CheckAutoResult(ResultCode: HResult): HResult;
|
|
{$IF Defined(PIC) or Defined(PUREPASCAL)}
|
|
begin
|
|
if ResultCode < 0 then
|
|
begin
|
|
if Assigned(SafeCallErrorProc) then
|
|
SafeCallErrorProc(ResultCode, Pointer(-1)); // loses error address
|
|
Error(reSafeCallError);
|
|
end;
|
|
Result := ResultCode;
|
|
end;
|
|
{$ELSE}
|
|
asm
|
|
TEST EAX,EAX
|
|
JNS @@2
|
|
MOV ECX,SafeCallErrorProc
|
|
TEST ECX,ECX
|
|
JE @@1
|
|
MOV EDX,[ESP]
|
|
CALL ECX
|
|
@@1: MOV AL,reSafeCallError
|
|
JMP Error
|
|
@@2:
|
|
end;
|
|
{$IFEND}
|
|
|
|
function CompToDouble(Value: Comp): Double; cdecl;
|
|
begin
|
|
Result := Value;
|
|
end;
|
|
|
|
procedure DoubleToComp(Value: Double; var Result: Comp); cdecl;
|
|
begin
|
|
Result := Value;
|
|
end;
|
|
|
|
function CompToCurrency(Value: Comp): Currency; cdecl;
|
|
begin
|
|
Result := Value;
|
|
end;
|
|
|
|
procedure CurrencyToComp(Value: Currency; var Result: Comp); cdecl;
|
|
begin
|
|
Result := Value;
|
|
end;
|
|
|
|
function GetMemory(Size: Integer): Pointer; cdecl;
|
|
begin
|
|
Result := MemoryManager.GetMem(Size);
|
|
end;
|
|
|
|
function FreeMemory(P: Pointer): Integer; cdecl;
|
|
begin
|
|
if P = nil then
|
|
Result := 0
|
|
else
|
|
Result := MemoryManager.FreeMem(P);
|
|
end;
|
|
|
|
function ReallocMemory(P: Pointer; Size: Integer): Pointer; cdecl;
|
|
begin
|
|
Result := MemoryManager.ReallocMem(P, Size);
|
|
end;
|
|
|
|
procedure SetLineBreakStyle(var T: Text; Style: TTextLineBreakStyle);
|
|
begin
|
|
if TTextRec(T).Mode = fmClosed then
|
|
TTextRec(T).Flags := TTextRec(T).Flags or (tfCRLF * Byte(Style))
|
|
else
|
|
SetInOutRes(107); // can't change mode of open file
|
|
end;
|
|
|
|
// UnicodeToUTF8(3):
|
|
// Scans the source data to find the null terminator, up to MaxBytes
|
|
// Dest must have MaxBytes available in Dest.
|
|
|
|
function UnicodeToUtf8(Dest: PChar; Source: PWideChar; MaxBytes: Integer): Integer;
|
|
var
|
|
len: Cardinal;
|
|
begin
|
|
len := 0;
|
|
if Source <> nil then
|
|
while Source[len] <> #0 do
|
|
Inc(len);
|
|
Result := UnicodeToUtf8(Dest, MaxBytes, Source, len);
|
|
end;
|
|
|
|
// UnicodeToUtf8(4):
|
|
// MaxDestBytes includes the null terminator (last char in the buffer will be set to null)
|
|
// Function result includes the null terminator.
|
|
// Nulls in the source data are not considered terminators - SourceChars must be accurate
|
|
|
|
function UnicodeToUtf8(Dest: PChar; MaxDestBytes: Cardinal; Source: PWideChar; SourceChars: Cardinal): Cardinal;
|
|
var
|
|
i, count: Cardinal;
|
|
c: Cardinal;
|
|
begin
|
|
Result := 0;
|
|
if Source = nil then Exit;
|
|
count := 0;
|
|
i := 0;
|
|
if Dest <> nil then
|
|
begin
|
|
while (i < SourceChars) and (count < MaxDestBytes) do
|
|
begin
|
|
c := Cardinal(Source[i]);
|
|
Inc(i);
|
|
if c <= $7F then
|
|
begin
|
|
Dest[count] := Char(c);
|
|
Inc(count);
|
|
end
|
|
else if c > $7FF then
|
|
begin
|
|
if count + 3 > MaxDestBytes then
|
|
break;
|
|
Dest[count] := Char($E0 or (c shr 12));
|
|
Dest[count+1] := Char($80 or ((c shr 6) and $3F));
|
|
Dest[count+2] := Char($80 or (c and $3F));
|
|
Inc(count,3);
|
|
end
|
|
else // $7F < Source[i] <= $7FF
|
|
begin
|
|
if count + 2 > MaxDestBytes then
|
|
break;
|
|
Dest[count] := Char($C0 or (c shr 6));
|
|
Dest[count+1] := Char($80 or (c and $3F));
|
|
Inc(count,2);
|
|
end;
|
|
end;
|
|
if count >= MaxDestBytes then count := MaxDestBytes-1;
|
|
Dest[count] := #0;
|
|
end
|
|
else
|
|
begin
|
|
while i < SourceChars do
|
|
begin
|
|
c := Integer(Source[i]);
|
|
Inc(i);
|
|
if c > $7F then
|
|
begin
|
|
if c > $7FF then
|
|
Inc(count);
|
|
Inc(count);
|
|
end;
|
|
Inc(count);
|
|
end;
|
|
end;
|
|
Result := count+1; // convert zero based index to byte count
|
|
end;
|
|
|
|
function Utf8ToUnicode(Dest: PWideChar; Source: PChar; MaxChars: Integer): Integer;
|
|
var
|
|
len: Cardinal;
|
|
begin
|
|
len := 0;
|
|
if Source <> nil then
|
|
while Source[len] <> #0 do
|
|
Inc(len);
|
|
Result := Utf8ToUnicode(Dest, MaxChars, Source, len);
|
|
end;
|
|
|
|
function Utf8ToUnicode(Dest: PWideChar; MaxDestChars: Cardinal; Source: PChar; SourceBytes: Cardinal): Cardinal;
|
|
var
|
|
i, count: Cardinal;
|
|
c: Byte;
|
|
wc: Cardinal;
|
|
begin
|
|
if Source = nil then
|
|
begin
|
|
Result := 0;
|
|
Exit;
|
|
end;
|
|
Result := Cardinal(-1);
|
|
count := 0;
|
|
i := 0;
|
|
if Dest <> nil then
|
|
begin
|
|
while (i < SourceBytes) and (count < MaxDestChars) do
|
|
begin
|
|
wc := Cardinal(Source[i]);
|
|
Inc(i);
|
|
if (wc and $80) <> 0 then
|
|
begin
|
|
wc := wc and $3F;
|
|
if i > SourceBytes then Exit; // incomplete multibyte char
|
|
if (wc and $20) <> 0 then
|
|
begin
|
|
c := Byte(Source[i]);
|
|
Inc(i);
|
|
if (c and $C0) <> $80 then Exit; // malformed trail byte or out of range char
|
|
if i > SourceBytes then Exit; // incomplete multibyte char
|
|
wc := (wc shl 6) or (c and $3F);
|
|
end;
|
|
c := Byte(Source[i]);
|
|
Inc(i);
|
|
if (c and $C0) <> $80 then Exit; // malformed trail byte
|
|
|
|
Dest[count] := WideChar((wc shl 6) or (c and $3F));
|
|
end
|
|
else
|
|
Dest[count] := WideChar(wc);
|
|
Inc(count);
|
|
end;
|
|
if count >= MaxDestChars then count := MaxDestChars-1;
|
|
Dest[count] := #0;
|
|
end
|
|
else
|
|
begin
|
|
while (i <= SourceBytes) do
|
|
begin
|
|
c := Byte(Source[i]);
|
|
Inc(i);
|
|
if (c and $80) <> 0 then
|
|
begin
|
|
if (c and $F0) = $F0 then Exit; // too many bytes for UCS2
|
|
if (c and $40) = 0 then Exit; // malformed lead byte
|
|
if i > SourceBytes then Exit; // incomplete multibyte char
|
|
|
|
if (Byte(Source[i]) and $C0) <> $80 then Exit; // malformed trail byte
|
|
Inc(i);
|
|
if i > SourceBytes then Exit; // incomplete multibyte char
|
|
if ((c and $20) <> 0) and ((Byte(Source[i]) and $C0) <> $80) then Exit; // malformed trail byte
|
|
Inc(i);
|
|
end;
|
|
Inc(count);
|
|
end;
|
|
end;
|
|
Result := count+1;
|
|
end;
|
|
|
|
function Utf8Encode(const WS: WideString): UTF8String;
|
|
var
|
|
L: Integer;
|
|
Temp: UTF8String;
|
|
begin
|
|
Result := '';
|
|
if WS = '' then Exit;
|
|
SetLength(Temp, Length(WS) * 3); // SetLength includes space for null terminator
|
|
|
|
L := UnicodeToUtf8(PChar(Temp), Length(Temp)+1, PWideChar(WS), Length(WS));
|
|
if L > 0 then
|
|
SetLength(Temp, L-1)
|
|
else
|
|
Temp := '';
|
|
Result := Temp;
|
|
end;
|
|
|
|
function Utf8Decode(const S: UTF8String): WideString;
|
|
var
|
|
L: Integer;
|
|
Temp: WideString;
|
|
begin
|
|
Result := '';
|
|
if S = '' then Exit;
|
|
SetLength(Temp, Length(S));
|
|
|
|
L := Utf8ToUnicode(PWideChar(Temp), Length(Temp)+1, PChar(S), Length(S));
|
|
if L > 0 then
|
|
SetLength(Temp, L-1)
|
|
else
|
|
Temp := '';
|
|
Result := Temp;
|
|
end;
|
|
|
|
function AnsiToUtf8(const S: string): UTF8String;
|
|
begin
|
|
Result := Utf8Encode(S);
|
|
end;
|
|
|
|
function Utf8ToAnsi(const S: UTF8String): string;
|
|
begin
|
|
Result := Utf8Decode(S);
|
|
end;
|
|
|
|
{$IFDEF LINUX}
|
|
|
|
function GetCPUType: Integer;
|
|
asm
|
|
PUSH EBX
|
|
// this code assumes ESP is 4 byte aligned
|
|
// test for 80386: see if bit #18 of EFLAGS (Alignment fault) can be toggled
|
|
PUSHF
|
|
POP EAX
|
|
MOV ECX, EAX
|
|
XOR EAX, $40000 // flip AC bit in EFLAGS
|
|
PUSH EAX
|
|
POPF
|
|
PUSHF
|
|
POP EAX
|
|
XOR EAX, ECX // zero = 80386 CPU (can't toggle AC bit)
|
|
MOV EAX, CPUi386
|
|
JZ @@Exit
|
|
PUSH ECX
|
|
POPF // restore original flags before next test
|
|
|
|
// test for 80486: see if bit #21 of EFLAGS (CPUID supported) can be toggled
|
|
MOV EAX, ECX // get original EFLAGS
|
|
XOR EAX, $200000 // flip CPUID bit in EFLAGS
|
|
PUSH EAX
|
|
POPF
|
|
PUSHF
|
|
POP EAX
|
|
XOR EAX, ECX // zero = 80486 (can't toggle EFLAGS bit #21)
|
|
MOV EAX, CPUi486
|
|
JZ @@Exit
|
|
|
|
// Use CPUID instruction to get CPU family
|
|
XOR EAX, EAX
|
|
CPUID
|
|
CMP EAX, 1
|
|
JL @@Exit // unknown processor response: report as 486
|
|
XOR EAX, EAX
|
|
INC EAX // we only care about info level 1
|
|
CPUID
|
|
AND EAX, $F00
|
|
SHR EAX, 8
|
|
// Test8086 values are one less than the CPU model number, for historical reasons
|
|
DEC EAX
|
|
|
|
@@Exit:
|
|
POP EBX
|
|
end;
|
|
|
|
|
|
const
|
|
sResSymExport = '@Sysinit@ResSym';
|
|
sResStrExport = '@Sysinit@ResStr';
|
|
sResHashExport = '@Sysinit@ResHash';
|
|
|
|
type
|
|
TElf32Sym = record
|
|
Name: Cardinal;
|
|
Value: Pointer;
|
|
Size: Cardinal;
|
|
Info: Byte;
|
|
Other: Byte;
|
|
Section: Word;
|
|
end;
|
|
PElf32Sym = ^TElf32Sym;
|
|
|
|
TElfSymTab = array [0..0] of TElf32Sym;
|
|
PElfSymTab = ^TElfSymTab;
|
|
|
|
TElfWordTab = array [0..2] of Cardinal;
|
|
PElfWordTab = ^TElfWordTab;
|
|
|
|
|
|
{ If Name encodes a numeric identifier, return it, else return -1. }
|
|
function NameToId(Name: PChar): Longint;
|
|
var digit: Longint;
|
|
begin
|
|
if Longint(Name) and $ffff0000 = 0 then
|
|
begin
|
|
Result := Longint(Name) and $ffff;
|
|
end
|
|
else if Name^ = '#' then
|
|
begin
|
|
Result := 0;
|
|
inc (Name);
|
|
while (Ord(Name^) <> 0) do
|
|
begin
|
|
digit := Ord(Name^) - Ord('0');
|
|
if (LongWord(digit) > 9) then
|
|
begin
|
|
Result := -1;
|
|
exit;
|
|
end;
|
|
Result := Result * 10 + digit;
|
|
inc (Name);
|
|
end;
|
|
end
|
|
else
|
|
Result := -1;
|
|
end;
|
|
|
|
|
|
// Return ELF hash value for NAME converted to lower case.
|
|
function ElfHashLowercase(Name: PChar): Cardinal;
|
|
var
|
|
g: Cardinal;
|
|
c: Char;
|
|
begin
|
|
Result := 0;
|
|
while name^ <> #0 do
|
|
begin
|
|
c := name^;
|
|
case c of
|
|
'A'..'Z': Inc(c, Ord('a') - Ord('A'));
|
|
end;
|
|
Result := (Result shl 4) + Ord(c);
|
|
g := Result and $f0000000;
|
|
Result := (Result xor (g shr 24)) and not g;
|
|
Inc(name);
|
|
end;
|
|
end;
|
|
|
|
type
|
|
PFindResourceCache = ^TFindResourceCache;
|
|
TFindResourceCache = record
|
|
ModuleHandle: HMODULE;
|
|
Version: Cardinal;
|
|
SymbolTable: PElfSymTab;
|
|
StringTable: PChar;
|
|
HashTable: PElfWordTab;
|
|
BaseAddress: Cardinal;
|
|
end;
|
|
|
|
threadvar
|
|
FindResourceCache: TFindResourceCache;
|
|
|
|
function GetResourceCache(ModuleHandle: HMODULE): PFindResourceCache;
|
|
var
|
|
info: TDLInfo;
|
|
begin
|
|
Result := @FindResourceCache;
|
|
if (ModuleHandle <> Result^.ModuleHandle) or (ModuleCacheVersion <> Result^.Version) then
|
|
begin
|
|
Result^.SymbolTable := dlsym(ModuleHandle, sResSymExport);
|
|
Result^.StringTable := dlsym(ModuleHandle, sResStrExport);
|
|
Result^.HashTable := dlsym(ModuleHandle, sResHashExport);
|
|
Result^.ModuleHandle := ModuleHandle;
|
|
if (dladdr(Result^.HashTable, Info) = 0) or (Info.BaseAddress = ExeBaseAddress) then
|
|
Result^.BaseAddress := 0 // if it's not in a library, assume the exe
|
|
else
|
|
Result^.BaseAddress := Cardinal(Info.BaseAddress);
|
|
Result^.Version := ModuleCacheVersion;
|
|
end;
|
|
end;
|
|
|
|
function FindResource(ModuleHandle: HMODULE; ResourceName: PChar; ResourceType: PChar): TResourceHandle;
|
|
var
|
|
P: PFindResourceCache;
|
|
nid, tid: Longint;
|
|
ucs2_key: array [0..2] of WideChar;
|
|
key: array [0..127] of Char;
|
|
len: Integer;
|
|
pc: PChar;
|
|
ch: Char;
|
|
nbucket: Cardinal;
|
|
bucket, chain: PElfWordTab;
|
|
syndx: Cardinal;
|
|
begin
|
|
Result := 0;
|
|
if ResourceName = nil then Exit;
|
|
P := GetResourceCache(ModuleHandle);
|
|
|
|
tid := NameToId (ResourceType);
|
|
if tid = -1 then Exit; { not supported (yet?) }
|
|
|
|
{ This code must match util-common/elfres.c }
|
|
nid := NameToId (ResourceName);
|
|
if nid = -1 then
|
|
begin
|
|
ucs2_key[0] := WideChar(2*tid+2);
|
|
ucs2_key[1] := WideChar(0);
|
|
len := UnicodeToUtf8 (key, ucs2_key, SizeOf (key));
|
|
pc := key+len;
|
|
while Ord(ResourceName^) <> 0 do
|
|
begin
|
|
ch := ResourceName^;
|
|
if Ord(ch) > 127 then exit; { insist on 7bit ASCII for now }
|
|
if ('A' <= ch) and (ch <= 'Z') then Inc(ch, Ord('a') - Ord('A'));
|
|
pc^ := ch;
|
|
inc (pc);
|
|
if pc = key + SizeOf(key) then exit;
|
|
inc (ResourceName);
|
|
end;
|
|
pc^ := Char(0);
|
|
end
|
|
else
|
|
begin
|
|
ucs2_key[0] := WideChar(2*tid+1);
|
|
ucs2_key[1] := WideChar(nid);
|
|
ucs2_key[2] := WideChar(0);
|
|
UnicodeToUtf8 (key, ucs2_key, SizeOf (key));
|
|
end;
|
|
|
|
with P^ do
|
|
begin
|
|
nbucket := HashTable[0];
|
|
// nsym := HashTable[1];
|
|
bucket := @HashTable[2];
|
|
chain := @HashTable[2+nbucket];
|
|
|
|
syndx := bucket[ElfHashLowercase(key) mod nbucket];
|
|
while (syndx <> 0)
|
|
and (strcasecmp(key, @StringTable[SymbolTable[syndx].Name]) <> 0) do
|
|
syndx := chain[syndx];
|
|
|
|
if syndx = 0 then
|
|
Result := 0
|
|
else
|
|
Result := TResourceHandle(@SymbolTable[syndx]);
|
|
end;
|
|
end;
|
|
|
|
function LoadResource(ModuleHandle: HMODULE; ResHandle: TResourceHandle): HGLOBAL;
|
|
var
|
|
P: PFindResourceCache;
|
|
begin
|
|
if ResHandle <> 0 then
|
|
begin
|
|
P := GetResourceCache(ModuleHandle);
|
|
Result := HGLOBAL(PElf32Sym(ResHandle)^.Value);
|
|
Inc (Cardinal(Result), P^.BaseAddress);
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function SizeofResource(ModuleHandle: HMODULE; ResHandle: TResourceHandle): Integer;
|
|
begin
|
|
if ResHandle <> 0 then
|
|
Result := PElf32Sym(ResHandle)^.Size
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function LockResource(ResData: HGLOBAL): Pointer;
|
|
begin
|
|
Result := Pointer(ResData);
|
|
end;
|
|
|
|
function UnlockResource(ResData: HGLOBAL): LongBool;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
function FreeResource(ResData: HGLOBAL): LongBool;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ ResString support function }
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
function LoadResString(ResStringRec: PResStringRec): string;
|
|
var
|
|
Buffer: array [0..1023] of char;
|
|
begin
|
|
if ResStringRec = nil then Exit;
|
|
if ResStringRec.Identifier < 64*1024 then
|
|
SetString(Result, Buffer,
|
|
LoadString(FindResourceHInstance(ResStringRec.Module^),
|
|
ResStringRec.Identifier, Buffer, SizeOf(Buffer)))
|
|
else
|
|
Result := PChar(ResStringRec.Identifier);
|
|
end;
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
|
|
const
|
|
ResStringTableLen = 16;
|
|
|
|
type
|
|
ResStringTable = array [0..ResStringTableLen-1] of LongWord;
|
|
|
|
function LoadResString(ResStringRec: PResStringRec): string;
|
|
var
|
|
Handle: TResourceHandle;
|
|
Tab: ^ResStringTable;
|
|
ResMod: HMODULE;
|
|
begin
|
|
if ResStringRec = nil then Exit;
|
|
ResMod := FindResourceHInstance(ResStringRec^.Module^);
|
|
Handle := FindResource(ResMod,
|
|
PChar(ResStringRec^.Identifier div ResStringTableLen),
|
|
PChar(6)); // RT_STRING
|
|
Tab := Pointer(LoadResource(ResMod, Handle));
|
|
if Tab = nil then
|
|
Result := ''
|
|
else
|
|
Result := PChar (Tab) + Tab[ResStringRec^.Identifier mod ResStringTableLen];
|
|
end;
|
|
|
|
procedure DbgUnlockX;
|
|
begin
|
|
if Assigned(DbgUnlockXProc) then
|
|
DbgUnlockXProc;
|
|
end;
|
|
|
|
{ The Win32 program loader sets up the first 64k of process address space
|
|
with no read or write access, to help detect use of invalid pointers
|
|
(whose integer value is 0..64k). Linux doesn't do this.
|
|
|
|
Parts of the Delphi RTL and IDE design environment
|
|
rely on the notion that pointer values in the [0..64k] range are
|
|
invalid pointers. To accomodate this in Linux, we reserve the range
|
|
at startup. If the range is already allocated, we keep going anyway. }
|
|
|
|
var
|
|
ZeroPageReserved: Boolean = False;
|
|
|
|
procedure ReserveZeroPage;
|
|
const
|
|
PROT_NONE = 0;
|
|
MAP_PRIVATE = $02;
|
|
MAP_FIXED = $10;
|
|
MAP_ANONYMOUS = $20;
|
|
var
|
|
P: Pointer;
|
|
begin
|
|
if IsLibrary then Exit; // page reserve is app's job, not .so's
|
|
|
|
if not ZeroPageReserved then
|
|
begin
|
|
P := mmap(nil, High(Word), PROT_NONE,
|
|
MAP_ANONYMOUS or MAP_PRIVATE or MAP_FIXED, 0, 0);
|
|
ZeroPageReserved := P = nil;
|
|
if (Integer(P) <> -1) and (P <> nil) then // we didn't get it
|
|
munmap(P, High(Word));
|
|
end;
|
|
end;
|
|
|
|
procedure ReleaseZeroPage;
|
|
begin
|
|
if ZeroPageReserved then
|
|
begin
|
|
munmap(nil, High(Word) - 4096);
|
|
ZeroPageReserved := False;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function PUCS4Chars(const S: UCS4String): PUCS4Char;
|
|
const
|
|
Null: UCS4Char = 0;
|
|
PNull: PUCS4Char = @Null;
|
|
begin
|
|
if Length(S) > 0 then
|
|
Result := @S[0]
|
|
else
|
|
Result := PNull;
|
|
end;
|
|
|
|
function WideStringToUCS4String(const S: WideString): UCS4String;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
SetLength(Result, Length(S) + 1);
|
|
for I := 0 to Length(S) - 1 do
|
|
Result[I] := UCS4Char(S[I + 1]);
|
|
Result[Length(S)] := 0;
|
|
end;
|
|
|
|
function UCS4StringToWidestring(const S: UCS4String): WideString;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
SetLength(Result, Length(S));
|
|
for I := 0 to Length(S)-1 do
|
|
Result[I+1] := WideChar(S[I]);
|
|
Result[Length(S)] := #0;
|
|
end;
|
|
|
|
var SaveCmdShow : Integer = -1;
|
|
function CmdShow: Integer;
|
|
var
|
|
SI: TStartupInfo;
|
|
begin
|
|
if SaveCmdShow < 0 then
|
|
begin
|
|
SaveCmdShow := 10; { SW_SHOWDEFAULT }
|
|
GetStartupInfo(SI);
|
|
if SI.dwFlags and 1 <> 0 then { STARTF_USESHOWWINDOW }
|
|
SaveCmdShow := SI.wShowWindow;
|
|
end;
|
|
Result := SaveCmdShow;
|
|
end;
|
|
|
|
{X} // convert var CmdLine : PChar to a function:
|
|
{X} function CmdLine : PChar;
|
|
{X} begin
|
|
{X} Result := GetCommandLine;
|
|
{X} end;
|
|
|
|
initialization
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
{$IFDEF USE_PROCESS_HEAP}
|
|
HeapHandle := GetProcessHeap;
|
|
{$ELSE}
|
|
HeapHandle := HeapCreate( 0, 0, 0 );
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
//{X (initialized statically} FileMode := 2;
|
|
{$ELSE}
|
|
FileMode := 2;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF LINUX}
|
|
FileAccessRights := S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH or S_IWOTH;
|
|
Test8086 := GetCPUType;
|
|
IsConsole := True;
|
|
FindResourceCache.ModuleHandle := LongWord(-1);
|
|
ReserveZeroPage;
|
|
{$ELSE}
|
|
//{X (initialized statically} Test8086 := 2;
|
|
{$ENDIF}
|
|
|
|
DispCallByIDProc := @_DispCallByIDError;
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
//{X} if _isNECWindows then _FpuMaskInit;
|
|
{$ENDIF}
|
|
//{X} _FpuInit();
|
|
|
|
TTextRec(Input).Mode := fmClosed;
|
|
TTextRec(Output).Mode := fmClosed;
|
|
TTextRec(ErrOutput).Mode := fmClosed;
|
|
InitVariantManager;
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
{X- CmdLine := GetCommandLine; converted to a function }
|
|
{X- CmdShow := GetCmdShow; converted to a function }
|
|
{$ENDIF}
|
|
MainThreadID := GetCurrentThreadID;
|
|
|
|
{$IFDEF LINUX}
|
|
// Ensure DbgUnlockX is linked in, calling it now does nothing
|
|
DbgUnlockX;
|
|
{$ENDIF}
|
|
|
|
finalization
|
|
{X+}
|
|
{X} CloseInputOutput;
|
|
{X-
|
|
Close(Input);
|
|
Close(Output);
|
|
Close(ErrOutput);
|
|
X+}
|
|
{$IFDEF LINUX}
|
|
ReleaseZeroPage;
|
|
{$ENDIF}
|
|
{$IFDEF MSWINDOWS}
|
|
{X UninitAllocator; - replaced with call to UninitMemoryManager handler. }
|
|
UninitMemoryManager;
|
|
{$ENDIF}
|
|
end.
|
|
|