{*******************************************************} // XCL version of System { } // unit. Created Jun-2000 { Borland Delphi Runtime Library } // (C) by Kladov Vladimir { System Unit } // { } // purpose: make XCL Delphi { Copyright (C) 1988,99 Inprise Corporation } // programs even smaller. { } // {*******************************************************} // Changes are marked as {X} 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-,S-} { L- should never be specified. The IDE needs to find debug hook (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 const { 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 } { vt_decimal $e } { undefined $f } { vt_i1 $10 } varByte = $0011; { vt_ui1 } { vt_ui2 $12 } { vt_ui4 $13 } { vt_i8 $14 } { if adding new items, update varLast, BaseTypeMap and OpTypeMap } varStrArg = $0048; { vt_clsid } varString = $0100; { Pascal string; not OLE compatible } varAny = $0101; 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; {$EXTERNALSYM HRESULT} HRESULT = type Longint; { from WTYPES.H } {$EXTERNALSYM IUnknown} {$EXTERNALSYM IDispatch} 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; 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; IUnknown = interface ['{00000000-0000-0000-C000-000000000046}'] function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end; 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; TInterfacedObject = class(TObject, IUnknown) 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; TVarArrayBound = packed record ElementCount: Integer; LowBound: Integer; end; PVarArray = ^TVarArray; TVarArray = packed record DimCount: Word; Flags: Word; ElementSize: Integer; LockCount: Integer; Data: Pointer; Bounds: array[0..255] of TVarArrayBound; end; PVarData = ^TVarData; TVarData = packed record VType: Word; Reserved1, Reserved2, Reserved3: Word; case Integer of varSmallint: (VSmallint: Smallint); varInteger: (VInteger: Integer); varSingle: (VSingle: Single); varDouble: (VDouble: Double); varCurrency: (VCurrency: Currency); varDate: (VDate: Double); varOleStr: (VOleStr: PWideChar); varDispatch: (VDispatch: Pointer); varError: (VError: LongWord); varBoolean: (VBoolean: WordBool); varUnknown: (VUnknown: Pointer); varByte: (VByte: Byte); varString: (VString: Pointer); varAny: (VAny: Pointer); varArray: (VArray: PVarArray); varByRef: (VPointer: Pointer); end; PShortString = ^ShortString; PAnsiString = ^AnsiString; PWideString = ^WideString; PString = PAnsiString; PExtended = ^Extended; PCurrency = ^Currency; PVariant = ^Variant; POleVariant = ^OleVariant; PInt64 = ^Int64; TDateTime = type Double; PDateTime = ^TDateTime; 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; PackageUnitEntry = packed record Init, FInit : procedure; 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; const advapi32 = 'advapi32.dll'; kernel = 'kernel32.dll'; user = 'user32.dll'; oleaut = 'oleaut32.dll'; {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-}////////////////////////////////////////////////////////////////////// {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} // following variables are converted to a functions: {X} function CmdShow : Integer; {X} function CmdLine : PChar; {X} procedure VarCastError; {X} procedure VarInvalidOp; {X} procedure DummyProc; // empty procedure {X} procedure VariantAddRef; {X} // procedure to refer to _VarAddRef if SysVarnt.pas is in use {X} var VarAddRefProc : procedure = DummyProc; {X} procedure VariantClr; {X} // procedure to refer to _VarClr if SysVarnt.pas is in use {X} var VarClrProc : 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} // 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; {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 InitUnitsLight( Table : PUnitEntryTable; Idx, Count : Integer ); {X} procedure InitUnitsHard( Table : PUnitEntryTable; Idx, Count : Integer ); {X} var InitUnitsProc : procedure( Table : PUnitEntryTable; Idx, Count : Integer ) {X} = InitUnitsLight; {X} procedure FInitUnitsLight; {X} procedure FInitUnitsHard; {X} var FInitUnitsProc : procedure = FInitUnitsLight; {X} procedure SetExceptionHandler; {X} procedure UnsetExceptionHandler; {X} var UnsetExceptionHandlerProc : procedure = DummyProc; {X} var UnloadResProc: procedure = DummyProc; function RaiseList: Pointer; { Stack of current exception objects } function SetRaiseList(NewPtr: Pointer): Pointer; { returns previous value } procedure SetInOutRes(NewValue: Integer); var ExceptProc: Pointer; { Unhandled exception handler } ErrorProc: Pointer; { Error handler procedure } ExceptClsProc: Pointer; { Map an OS Exception to a Delphi class reference } ExceptObjProc: Pointer; { Map an OS Exception to a Delphi class instance } ExceptionClass: TClass; { Exception base class (must be Exception) } SafeCallErrorProc: Pointer; { Safecall error handler } AssertErrorProc: Pointer; { Assertion error handler } AbstractErrorProc: Pointer; { Abstract method error handler } HPrevInst: LongWord; { 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 } {X CmdShow: Integer; { CmdShow parameter for CreateWindow - converted to a function X} {X CmdLine: PChar; { Command line pointer - converted to a function X} InitProc: Pointer; { Last installed initialization procedure } ExitCode: Integer; { Program result } ExitProc: Pointer; { Last installed exit procedure } ErrorAddr: Pointer; { Address of run-time error } RandSeed: Longint; { Base for random number generator } IsConsole: Boolean; { True if compiled as console app } IsMultiThread: Boolean; { True if more than one thread } FileMode: Byte {X} = 2; { Standard mode for opening files } Test8086: Byte {X} = 2; { Will always be 2 (386 or later) } Test8087: Byte {X} = 3; { Will always be 3 (387 or later) } TestFDIV: Shortint; { -1: Flawed Pentium, 0: Not determined, 1: Ok } Input: Text; { Standard input } Output: Text; { Standard output } ClearAnyProc: Pointer; { Handler clearing a varAny } ChangeAnyProc: Pointer; { Handler to change any to variant } RefAnyProc: Pointer; { Handler to add a reference to an varAny } 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 = 2; { Heap allocation flags, gmem_Moveable } DebugHook: Byte = 0; { 1 to notify debugger of non-Delphi exceptions >1 to notify debugger of exception unwinding } JITEnable: Byte = 0; { 1 to call UnhandledExceptionFilter if the exception is not a Pascal exception. >1 to call UnhandledExceptionFilter for all exceptions } NoErrMsg: Boolean = False; { True causes the base RTL to not display the message box when a run-time error occurs } var (* {X-} moved to SysVarnt.pas Unassigned: Variant; { Unassigned standard constant } Null: Variant; { Null standard constant } EmptyParam: OleVariant; { "Empty parameter" standard constant which can be passed as an optional parameter on a dual interface. } {X+} *) AllocMemCount: Integer; { Number of allocated memory blocks } AllocMemSize: Integer; { Total size of allocated memory blocks } { Memory manager support } 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; function GetHeapStatus: THeapStatus; { Thread support } type TThreadFunc = function(Parameter: Pointer): Integer; function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord; ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord; var ThreadId: LongWord): Integer; procedure EndThread(ExitCode: Integer); { Standard procedures and functions } procedure _ChDir(const S: string); procedure __Flush(var F: Text); procedure _LGetDir(D: Byte; var S: string); procedure _SGetDir(D: Byte; var S: ShortString); function IOResult: Integer; procedure _MkDir(const S: string); procedure Move(const Source; var Dest; Count: Integer); function ParamCount: Integer; function ParamStr(Index: Integer): string; procedure Randomize; procedure _RmDir(const S: string); function UpCase(Ch: Char): Char; { Control 8087 control word } procedure Set8087CW(NewCW: Word); { Wide character support procedures and functions } 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; { 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 support procedures and functions } procedure _VarClear(var V : Variant); procedure _VarCopy(var Dest : Variant; const Source: Variant); procedure _VarCast(var Dest : Variant; const Source: Variant; VarType: Integer); procedure _VarCastOle(var Dest : Variant; const Source: Variant; VarType: Integer); procedure VarCopyNoInd(var Dest: Variant; const Source: Variant); function VarType(const V: Variant): Integer; function VarAsType(const V: Variant; VarType: Integer): Variant; function VarIsEmpty(const V: Variant): Boolean; function VarIsNull(const V: Variant): Boolean; function VarToStr(const V: Variant): string; function VarFromDateTime(DateTime: TDateTime): Variant; function VarToDateTime(const V: Variant): TDateTime; { Variant array support procedures and functions } function VarArrayCreate(const Bounds: array of Integer; VarType: Integer): Variant; function VarArrayOf(const Values: array of Variant): Variant; procedure _VarArrayRedim(var A : Variant; HighBound: Integer); function VarArrayDimCount(const A: Variant): Integer; function VarArrayLowBound(const A: Variant; Dim: Integer): Integer; function VarArrayHighBound(const A: Variant; Dim: Integer): Integer; function VarArrayLock(const A: Variant): Pointer; procedure VarArrayUnlock(const A: Variant); function VarArrayRef(const A: Variant): Variant; function VarIsArray(const A: Variant): Boolean; { Variant IDispatch call support } procedure _DispInvokeError; var VarDispProc: Pointer = @_DispInvokeError; DispCallByIDProc: Pointer = @_DispInvokeError; { Package/Module registration and unregistration } type PLibModule = ^TLibModule; TLibModule = record Next: PLibModule; Instance: LongWord; CodeInstance: LongWord; DataInstance: LongWord; ResInstance: LongWord; Reserved: Integer; 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): 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; { ResString support function/record } type PResStringRec = ^TResStringRec; TResStringRec = packed record Module: ^Longint; Identifier: Integer; end; function LoadResString(ResStringRec: PResStringRec): string; { Procedures and functions that need compiler magic } procedure _COS; procedure _EXP; procedure _INT; procedure _SIN; procedure _FRAC; procedure _ROUND; procedure _TRUNC; procedure _AbstractError; procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer); procedure _Append; procedure _Assign(var T: Text; S: ShortString); procedure _BlockRead; procedure _BlockWrite; procedure _Close; procedure _PStrCat; procedure _PStrNCat; procedure _PStrCpy; procedure _PStrNCpy; procedure _EofFile; procedure _EofText; procedure _Eoln; procedure _Erase; procedure _FilePos; procedure _FileSize; procedure _FillChar; procedure _FreeMem; procedure _GetMem; procedure _ReallocMem; procedure _Halt; procedure _Halt0; procedure _Mark; procedure _PStrCmp; procedure _AStrCmp; procedure _RandInt; procedure _RandExt; procedure _ReadRec; procedure _ReadChar; procedure _ReadLong; procedure _ReadString; procedure _ReadCString; procedure _ReadLString; procedure _ReadExt; procedure _ReadLn; procedure _Rename; procedure _Release; procedure _ResetText(var T: Text); procedure _ResetFile; procedure _RewritText(var T: Text); procedure _RewritFile; procedure _RunError; procedure _Run0Error; procedure _Seek; procedure _SeekEof; procedure _SeekEoln; procedure _SetTextBuf; procedure _StrLong; procedure _Str0Long; procedure _Truncate; procedure _ValLong; procedure _WriteRec; procedure _WriteChar; procedure _Write0Char; procedure _WriteBool; procedure _Write0Bool; procedure _WriteLong; procedure _Write0Long; procedure _WriteString; procedure _Write0String; procedure _WriteCString; procedure _Write0CString; procedure _WriteLString; procedure _Write0LString; function _WriteVariant(var T: Text; const V: Variant; Width: Integer): Pointer; function _Write0Variant(var T: Text; const V: Variant): Pointer; procedure _Write2Ext; procedure _Write1Ext; procedure _Write0Ext; procedure _WriteLn; procedure __CToPasStr; procedure __CLenToPasStr; procedure __ArrayToPasStr; procedure __PasToCStr; procedure __IOTest; procedure _Flush(var F: Text); 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; procedure _StartExe; procedure _StartLib; procedure _PackageLoad (const Table : PackageInfo); procedure _PackageUnload(const Table : PackageInfo); procedure _InitResStrings; procedure _InitResStringImports; procedure _InitImports; procedure _InitWideStrings; procedure _ClassCreate; procedure _ClassDestroy; procedure _AfterConstruction; procedure _BeforeDestruction; procedure _IsClass; procedure _AsClass; procedure _RaiseExcept; procedure _RaiseAgain; procedure _DoneExcept; procedure _TryFinallyExit; procedure _CallDynaInst; procedure _CallDynaClass; procedure _FindDynaInst; procedure _FindDynaClass; procedure _LStrClr(var S: AnsiString); procedure _LStrArrayClr{var str: AnsiString; cnt: longint}; procedure _LStrAsg{var dest: AnsiString; source: AnsiString}; procedure _LStrLAsg{var dest: AnsiString; source: AnsiString}; 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{str: 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}; procedure _LStrAddRef{str: AnsiString}; procedure _LStrToPChar{str: 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 }; procedure _Pos{ substr : ShortString; s : ShortString ) : Integer}; procedure _SetLength{var s: ShortString; newLength: Integer}; procedure _SetString{var s: ShortString: buffer: PChar; len: Integer}; procedure UniqueString(var str: string); procedure _NewAnsiString{length: Longint}; { for debugger purposes only } 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 }; procedure _WStrClr(var S: WideString); procedure _WStrArrayClr(var StrArray; Count: Integer); procedure _WStrAsg(var Dest: WideString; const Source: WideString); procedure _WStrFromPCharLen(var Dest: WideString; Source: PAnsiChar; Length: Integer); procedure _WStrFromPWCharLen(var Dest: WideString; Source: PWideChar; Length: 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); function _WStrToPWChar(const S: WideString): PWideChar; function _WStrLen(const S: WideString): 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 _NewWideString(Length: Integer): PWideChar; 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; procedure _WStrAddRef{var str: WideString}; procedure _Initialize; procedure _InitializeArray; procedure _InitializeRecord; procedure _Finalize; procedure _FinalizeArray; procedure _FinalizeRecord; procedure _AddRef; procedure _AddRefArray; procedure _AddRefRecord; procedure _CopyArray; procedure _CopyRecord; procedure _CopyObject; procedure _New; procedure _Dispose; procedure _DispInvoke; cdecl; procedure _IntfDispCall; cdecl; procedure _IntfVarCall; cdecl; procedure _VarToInt; procedure _VarToBool; procedure _VarToReal; procedure _VarToCurr; 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: IUnknown; const V: Variant); procedure _VarToDisp(var Dispatch: IDispatch; const V: Variant); procedure _VarToDynArray(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer); procedure _VarFromInt; procedure _VarFromBool; procedure _VarFromReal; procedure _VarFromTDateTime; procedure _VarFromCurr; 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: IUnknown); 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 _VarAdd; procedure _VarSub; procedure _VarMul; procedure _VarDiv; procedure _VarMod; procedure _VarAnd; procedure _VarOr; procedure _VarXor; procedure _VarShl; procedure _VarShr; procedure _VarRDiv; procedure _VarCmp; procedure _VarNeg; procedure _VarNot; procedure _VarCopyNoInd; procedure _VarClr; procedure _VarAddRef; { 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 DynArrayToVariant(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer); procedure DynArrayFromVariant(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer); procedure _IntfClear(var Dest: IUnknown); procedure _IntfCopy(var Dest: IUnknown; const Source: IUnknown); procedure _IntfCast(var Dest: IUnknown; const Source: IUnknown; const IID: TGUID); procedure _IntfAddRef(const Dest: IUnknown); function _VarArrayGet(var A: Variant; IndexCount: Integer; Indices: Integer): Variant; cdecl; procedure _VarArrayPut(var A: Variant; const Value: Variant; IndexCount: Integer; Indices: Integer); cdecl; procedure _HandleAnyException; procedure _HandleOnException; procedure _HandleFinally; procedure _HandleAutoException; procedure _FSafeDivide; procedure _FSafeDivideR; procedure _CheckAutoResult; procedure FPower10; procedure TextStart; function CompToDouble(acomp: Comp): Double; cdecl; procedure DoubleToComp(adouble: Double; var result: Comp); cdecl; function CompToCurrency(acomp: Comp): Currency; cdecl; procedure CurrencyToComp(acurrency: 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; (* =================================================================== *) implementation uses SysInit; { Internal runtime error codes } const reOutOfMemory = 1; reInvalidPtr = 2; reDivByZero = 3; reRangeError = 4; reIntOverflow = 5; reInvalidOp = 6; reZeroDivide = 7; reOverflow = 8; reUnderflow = 9; reInvalidCast = 10; reAccessViolation = 11; reStackOverflow = 12; reControlBreak = 13; rePrivInstruction = 14; reVarTypeCast = 15; reVarInvalidOp = 16; reVarDispatch = 17; reVarArrayCreate = 18; reVarNotArray = 19; reVarArrayBounds = 20; reAssertionFailed = 21; reExternalException = 22; { not used here; in SysUtils } reIntfCastError = 23; reSafeCallError = 24; { this procedure should be at the very beginning of the } { text segment. it is only used by _RunError to find } { start address of the text segment so a nice error } { location can be shown. } procedure TextStart; begin end; { ----------------------------------------------------- } { NT Calls necessary for the .asm files } { ----------------------------------------------------- } 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; procedure CloseHandle; external kernel name 'CloseHandle'; procedure CreateFileA; external kernel name 'CreateFileA'; procedure DeleteFileA; external kernel name 'DeleteFileA'; procedure GetFileType; external kernel name 'GetFileType'; procedure GetSystemTime; external kernel name 'GetSystemTime'; procedure GetFileSize; external kernel name 'GetFileSize'; procedure GetStdHandle; external kernel name 'GetStdHandle'; //procedure GetStartupInfo; external kernel name 'GetStartupInfo'; procedure MoveFileA; external kernel name 'MoveFileA'; procedure RaiseException; external kernel name 'RaiseException'; procedure ReadFile; external kernel name 'ReadFile'; procedure RtlUnwind; external kernel name 'RtlUnwind'; procedure SetEndOfFile; external kernel name 'SetEndOfFile'; procedure SetFilePointer; external kernel name 'SetFilePointer'; procedure UnhandledExceptionFilter; external kernel name 'UnhandledExceptionFilter'; procedure WriteFile; 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'; 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 lstrlen(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'; //procedure VariantInit(var V: Variant); stdcall; // external oleaut name 'VariantInit'; function VariantClear(var V: Variant): Integer; stdcall; external oleaut name 'VariantClear'; function VariantCopy(var Dest: Variant; const Source: Variant): Integer; stdcall; external oleaut name 'VariantCopy'; function VariantCopyInd(var Dest: Variant; const Source: Variant): Integer; stdcall; external oleaut name 'VariantCopyInd'; //function VariantChangeType(var Dest: Variant; const Source: Variant; // Flags: Word; VarType: Word): Integer; stdcall; // external oleaut name 'VariantChangeType'; function VariantChangeTypeEx(var Dest: Variant; const Source: Variant; LCID: Integer; Flags: Word; VarType: Word): Integer; stdcall; external oleaut name 'VariantChangeTypeEx'; function SafeArrayCreate(VarType, DimCount: Integer; const Bounds): PVarArray; stdcall; external oleaut name 'SafeArrayCreate'; function SafeArrayRedim(VarArray: PVarArray; var NewBound: TVarArrayBound): Integer; stdcall; external oleaut name 'SafeArrayRedim'; function SafeArrayGetLBound(VarArray: PVarArray; Dim: Integer; var LBound: Integer): Integer; stdcall; external oleaut name 'SafeArrayGetLBound'; function SafeArrayGetUBound(VarArray: PVarArray; Dim: Integer; var UBound: Integer): Integer; stdcall; external oleaut name 'SafeArrayGetUBound'; function SafeArrayAccessData(VarArray: PVarArray; var Data: Pointer): Integer; stdcall; external oleaut name 'SafeArrayAccessData'; function SafeArrayUnaccessData(VarArray: PVarArray): Integer; stdcall; external oleaut name 'SafeArrayUnaccessData'; function SafeArrayGetElement(VarArray: PVarArray; Indices, Data: Pointer): Integer; stdcall; external oleaut name 'SafeArrayGetElement'; function SafeArrayPtrOfIndex(VarArray: PVarArray; Indices: Pointer; var pvData: Pointer): HResult; stdcall; external oleaut name 'SafeArrayPtrOfIndex'; function SafeArrayPutElement(VarArray: PVarArray; Indices, Data: Pointer): Integer; stdcall; external oleaut name 'SafeArrayPutElement'; function InterlockedIncrement(var Addend: Integer): Integer; stdcall; external kernel name 'InterlockedIncrement'; function InterlockedDecrement(var Addend: Integer): Integer; stdcall; external kernel name 'InterlockedDecrement'; 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; { ----------------------------------------------------- } { Memory manager } { ----------------------------------------------------- } procedure Error(errorCode: Byte); forward; {$I GETMEM.INC } {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+} procedure _GetMem; asm TEST EAX,EAX JE @@1 CALL MemoryManager.GetMem OR EAX,EAX JE @@2 @@1: RET @@2: MOV AL,reOutOfMemory JMP Error end; procedure _FreeMem; asm TEST EAX,EAX JE @@1 CALL MemoryManager.FreeMem OR EAX,EAX JNE @@2 @@1: RET @@2: MOV AL,reInvalidPtr JMP Error end; procedure _ReallocMem; 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; 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; 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-} threadvar RaiseListPtr: pointer; InOutRes: Integer; function RaiseList: Pointer; asm CALL SysInit.@GetTLS MOV EAX, [EAX].RaiseListPtr end; function SetRaiseList(NewPtr: Pointer): Pointer; asm MOV ECX, EAX CALL SysInit.@GetTLS MOV EDX, [EAX].RaiseListPtr MOV [EAX].RaiseListPtr, ECX MOV EAX, EDX end; { ----------------------------------------------------- } { local functions & procedures of the system unit } { ----------------------------------------------------- } procedure Error(errorCode: Byte); asm AND EAX,127 MOV ECX,ErrorProc TEST ECX,ECX JE @@term POP EDX CALL ECX @@term: DEC EAX MOV AL,byte ptr @@errorTable[EAX] JNS @@skip CALL SysInit.@GetTLS MOV EAX,[EAX].InOutRes @@skip: JMP _RunError @@errorTable: DB 203 { reOutOfMemory } DB 204 { reInvalidPtr } DB 200 { reDivByZero } DB 201 { reRangeError } { 210 abstract error } DB 215 { reIntOverflow } DB 207 { reInvalidOp } DB 200 { reZeroDivide } DB 205 { reOverflow } DB 206 { reUnderflow } DB 219 { reInvalidCast } DB 216 { Access violation } DB 202 { Stack overflow } DB 217 { Control-C } DB 218 { Privileged instruction } DB 220 { Invalid variant type cast } DB 221 { Invalid variant operation } DB 222 { No variant method call dispatcher } DB 223 { Cannot create variant array } DB 224 { Variant does not contain an array } DB 225 { Variant array bounds error } { 226 thread init failure } DB 227 { reAssertionFailed } DB 0 { reExternalException not used here; in SysUtils } DB 228 { reIntfCastError } DB 229 { reSafeCallError } 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; asm PUSH EAX CALL SysInit.@GetTLS POP [EAX].InOutRes end; procedure InOutError; asm CALL GetLastError JMP SetInOutRes end; procedure _ChDir(const S: string); begin if not SetCurrentDirectory(PChar(S)) then 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 __Flush( var f : Text ); external; { Assign } procedure _Flush( var f : Text ); external; { Assign } procedure _LGetDir(D: Byte; var S: string); var Drive: array[0..3] of Char; DirBuf, SaveBuf: array[0..259] 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; end; procedure _SGetDir(D: Byte; var S: ShortString); var L: string; begin GetDir(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; asm CALL SysInit.@GetTLS XOR EDX,EDX MOV ECX,[EAX].InOutRes MOV [EAX].InOutRes,EDX MOV EAX,ECX end; procedure _MkDir(const S: string); begin if not CreateDirectory(PChar(S), 0) then InOutError; end; procedure Move( const Source; var Dest; count : Integer ); 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; function GetParamStr(P: PChar; var Param: string): PChar; var Len: Integer; Buffer: array[0..4095] of Char; begin while True do begin while (P[0] <> #0) and (P[0] <= ' ') do Inc(P); if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break; end; Len := 0; while (P[0] > ' ') and (Len < SizeOf(Buffer)) do if P[0] = '"' then begin Inc(P); while (P[0] <> #0) and (P[0] <> '"') do begin Buffer[Len] := P[0]; Inc(Len); Inc(P); end; if P[0] <> #0 then Inc(P); end else begin Buffer[Len] := P[0]; Inc(Len); Inc(P); end; SetString(Param, Buffer, Len); Result := P; end; function ParamCount: Integer; var P: PChar; S: string; begin P := GetParamStr(GetCommandLine, S); Result := 0; while True do begin P := GetParamStr(P, S); if S = '' then Break; Inc(Result); end; end; function ParamStr(Index: Integer): string; var P: PChar; Buffer: array[0..260] of Char; begin 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; end; procedure _Pos{ substr : ShortString; s : ShortString ) : Integer}; asm { ->EAX Pointer to substr } { EDX Pointer to string } { <-EAX Position of substr in s or 0 } PUSH EBX PUSH ESI PUSH EDI MOV ESI,EAX { Point ESI to substr } MOV EDI,EDX { Point EDI to s } XOR ECX,ECX { ECX = Length(s) } MOV CL,[EDI] INC EDI { Point EDI to first char of s } PUSH EDI { remember s position to calculate index } XOR EDX,EDX { EDX = Length(substr) } MOV DL,[ESI] INC ESI { Point ESI to first char of 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 @@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 end; procedure _SetLength{var s: ShortString; newLength: Integer}; asm { -> EAX pointer to string } { EDX new length } MOV [EAX],DL { should also fill new space, parameter should be openstring } end; procedure _SetString{var s: ShortString: buffer: PChar; len: Integer}; asm { -> EAX pointer to string } { EDX pointer to buffer } { ECX len } MOV [EAX],CL TEST EDX,EDX JE @@noMove XCHG EAX,EDX INC EDX CALL Move @@noMove: end; procedure Randomize; var systemTime : record wYear : Word; wMonth : Word; wDayOfWeek : Word; wDay : Word; wHour : Word; wMinute : Word; wSecond : Word; wMilliSeconds: Word; reserved : array [0..7] of char; end; asm LEA EAX,systemTime PUSH EAX CALL GetSystemTime MOVZX EAX,systemTime.wHour IMUL EAX,60 ADD AX,systemTime.wMinute { sum = hours * 60 + minutes } IMUL EAX,60 XOR EDX,EDX MOV DX,systemTime.wSecond ADD EAX,EDX { sum = sum * 60 + seconds } IMUL EAX,1000 MOV DX,systemTime.wMilliSeconds ADD EAX,EDX { sum = sum * 1000 + milliseconds } MOV RandSeed,EAX end; procedure _RmDir(const S: string); begin if not RemoveDirectory(PChar(S)) then InOutError; end; function UpCase( ch : Char ) : Char; asm { -> AL Character } { <- AL Result } CMP AL,'a' JB @@exit CMP AL,'z' JA @@exit SUB AL,'a' - 'A' @@exit: end; procedure Set8087CW(NewCW: Word); asm MOV Default8087CW,AX FNCLEX // don't raise pending exceptions enabled by the new flags FLDCW Default8087CW end; { ----------------------------------------------------- } { functions & procedures that need compiler magic } { ----------------------------------------------------- } const cwChop : Word = $1F32; procedure _COS; asm FCOS FNSTSW AX SAHF JP @@outOfRange RET @@outOfRange: FSTP st(0) { for now, return 0. result would } FLDZ { have little significance anyway } end; procedure _EXP; asm { e**x = 2**(x*log2(e)) } 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; procedure _INT; asm SUB ESP,4 FSTCW [ESP] FWAIT FLDCW cwChop FRNDINT FWAIT FLDCW [ESP] ADD ESP,4 end; procedure _SIN; asm FSIN FNSTSW AX SAHF JP @@outOfRange RET @@outOfRange: FSTP st(0) { for now, return 0. result would } FLDZ { have little significance anyway } end; procedure _FRAC; asm FLD ST(0) SUB ESP,4 FSTCW [ESP] FWAIT FLDCW cwChop FRNDINT FWAIT FLDCW [ESP] ADD ESP,4 FSUB end; 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 FSTCW [ESP] FWAIT FLDCW cwChop FISTP qword ptr [ESP+4] FWAIT FLDCW [ESP] POP ECX POP EAX POP EDX end; procedure _AbstractError; asm CMP AbstractErrorProc, 0 JE @@NoAbstErrProc CALL AbstractErrorProc @@NoAbstErrProc: MOV EAX,210 JMP _RunError end; procedure _Append; external; { OpenText} procedure _Assign(var t: text; s: ShortString); external; {$L Assign } procedure _BlockRead; external; {$L BlockRea} procedure _BlockWrite; external; {$L BlockWri} procedure _Close; external; {$L Close } 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; asm { ->EAX = Pointer to dest string } { EDX = Pointer to source string } XOR ECX,ECX PUSH ESI PUSH EDI MOV CL,[EDX] MOV EDI,EAX INC ECX { we must copy len+1 bytes } MOV ESI,EDX MOV EAX,ECX SHR ECX,2 AND EAX,3 REP MOVSD MOV ECX,EAX REP MOVSB POP EDI POP ESI end; procedure _PStrNCpy; asm { ->EAX = Pointer to dest string } { EDX = Pointer to source string } { CL = Maximum length to copy (allocated size of dest - 1) } PUSH ESI PUSH EDI MOV EDI,EAX XOR EAX,EAX MOV ESI,EDX MOV AL,[EDX] CMP AL,CL JA @@trunc INC EAX MOV ECX,EAX AND EAX,3 SHR ECX,2 REP MOVSD MOV ECX,EAX REP MOVSB POP EDI POP ESI RET @@trunc: MOV [EDI],CL { result length is maxLen } INC ESI { advance pointers } INC EDI AND ECX,0FFH { should be cheaper than MOVZX } REP MOVSB { copy maxLen bytes } POP EDI POP ESI 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; procedure _EofFile; external; {$L EofFile } procedure _EofText; external; {$L EofText } procedure _Eoln; external; {$L Eoln } procedure _Erase; external; {$L Erase } procedure _FSafeDivide; external; {$L FDIV } procedure _FSafeDivideR; external; { FDIV } procedure _FilePos; external; {$L FilePos } procedure _FileSize; external; {$L FileSize} procedure _FillChar; 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; procedure _Mark; begin Error(reInvalidPtr); end; procedure _RandInt; asm { ->EAX Range } { <-EAX Result } IMUL EDX,RandSeed,08088405H INC EDX MOV RandSeed,EDX MUL EDX MOV EAX,EDX end; procedure _RandExt; const two2neg32: double = ((1.0/$10000) / $10000); // 2^-32 asm { FUNCTION _RandExt: Extended; } IMUL EDX,RandSeed,08088405H INC EDX MOV RandSeed,EDX FLD two2neg32 PUSH 0 PUSH EDX FILD qword ptr [ESP] ADD ESP,8 FMULP ST(1), ST(0) end; procedure _ReadRec; external; {$L ReadRec } procedure _ReadChar; external; {$L ReadChar} procedure _ReadLong; external; {$L ReadLong} procedure _ReadString; external; {$L ReadStri} procedure _ReadCString; external; { ReadStri} procedure _ReadExt; external; {$L ReadExt } procedure _ReadLn; external; {$L ReadLn } procedure _Rename; external; {$L Rename } procedure _Release; begin Error(reInvalidPtr); end; procedure _ResetText(var t: text); external; {$L OpenText} procedure _ResetFile; external; {$L OpenFile} procedure _RewritText(var t: text); external; { OpenText} procedure _RewritFile; external; { OpenFile} procedure _Seek; external; {$L Seek } procedure _SeekEof; external; {$L SeekEof } procedure _SeekEoln; external; {$L SeekEoln} procedure _SetTextBuf; external; {$L SetTextB} procedure _StrLong; 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; procedure _Str0Long; asm { ->EAX Value } { EDX Pointer to string } MOV ECX,EDX XOR EDX,EDX JMP _StrLong end; procedure _Truncate; external; {$L Truncate} procedure _ValLong; 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 JL @@overFlow @@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 RET @@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 JMP @@successExit end; procedure _WriteRec; external; {$L WriteRec} procedure _WriteChar; external; { WriteStr} procedure _Write0Char; external; { WriteStr} procedure _WriteBool; asm { PROCEDURE _WriteBool( VAR t: Text; val: Boolean; width: Longint); } { ->EAX Pointer to file record } { DL Boolean value } { ECX Field width } TEST DL,DL JE @@false MOV EDX,offset @trueString JMP _WriteString @@false: MOV EDX,offset @falseString JMP _WriteString @trueString: db 4,'TRUE' @falseString: db 5,'FALSE' end; procedure _Write0Bool; asm { PROCEDURE _Write0Bool( VAR t: Text; val: Boolean); } { ->EAX Pointer to file record } { DL Boolean value } XOR ECX,ECX JMP _WriteBool end; procedure _WriteLong; asm { PROCEDURE _WriteLong( VAR t: Text; val: Longint; with: Longint); } { ->EAX Pointer to file record } { EDX Value } { ECX Field width } SUB ESP,32 { VAR s: String[31]; } PUSH EAX PUSH ECX MOV EAX,EDX { Str( val : 0, s ); } XOR EDX,EDX CMP ECX,31 JG @@1 MOV EDX,ECX @@1: LEA ECX,[ESP+8] CALL _StrLong POP ECX POP EAX MOV EDX,ESP { Write( t, s : width );} CALL _WriteString ADD ESP,32 end; procedure _Write0Long; asm { PROCEDURE _Write0Long( VAR t: Text; val: Longint); } { ->EAX Pointer to file record } { EDX Value } XOR ECX,ECX JMP _WriteLong end; procedure _WriteString; external; {$L WriteStr} procedure _Write0String; external; { WriteStr} procedure _WriteCString; external; { WriteStr} procedure _Write0CString; external; { WriteStr} procedure _WriteBytes; external; { WriteStr} procedure _WriteSpaces; external; { WriteStr} 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; procedure _WriteLn; external; { WriteStr} procedure __CToPasStr; asm { ->EAX Pointer to destination } { EDX Pointer to source } PUSH EAX { save destination } MOV CL,255 @@loop: MOV CH,[EDX] { ch = *src++; } INC EDX TEST CH,CH { if (ch == 0) break } JE @@endLoop INC EAX { *++dest = ch; } MOV [EAX],CH DEC CL JNE @@loop @@endLoop: POP EDX SUB EAX,EDX MOV [EDX],AL end; procedure __CLenToPasStr; 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; procedure __ArrayToPasStr; asm { ->EAX Pointer to destination } { EDX Pointer to source } { ECX cnt } XCHG EAX,EDX { limit the length to 255 } CMP ECX,255 JBE @@skip MOV ECX,255 @@skip: MOV [EDX],CL { copy the source to destination + 1 } INC EDX JMP Move end; procedure __PasToCStr; asm { ->EAX Pointer to source } { EDX Pointer to destination } PUSH ESI PUSH EDI MOV ESI,EAX MOV EDI,EDX XOR ECX,ECX MOV CL,[ESI] INC ESI REP MOVSB MOV byte ptr [EDI],CL { Append terminator: CL is zero here } POP EDI POP ESI 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 _Str2Ext; external; {$L StrExt } procedure _Str0Ext; external; { StrExt } procedure _Str1Ext; external; { StrExt } procedure _ValExt; external; {$L ValExt } procedure _Pow10; external; {$L Pow10 } procedure FPower10; external; { Pow10 } procedure _Real2Ext; external; {$L Real2Ext} procedure _Ext2Real; external; {$L Ext2Real} 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 MemoryManager.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; 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; procedure _FpuMaskInit; const HKEY_LOCAL_MACHINE = $80000002; 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; procedure FpuInit; //const cwDefault: Word = $1332 { $133F}; asm FNINIT FWAIT FLDCW Default8087CW 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; asm mov eax,[eax] end; class function TObject.ClassName: ShortString; 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; class function TObject.ClassNameIs(const Name: string): Boolean; 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; class function TObject.ClassParent: TClass; asm MOV EAX,[EAX].vmtParent TEST EAX,EAX JE @@exit MOV EAX,[EAX] @@exit: end; class function TObject.NewInstance: TObject; asm PUSH EAX MOV EAX,[EAX].vmtInstanceSize CALL _GetMem MOV EDX,EAX POP EAX JMP TObject.InitInstance end; procedure TObject.FreeInstance; 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 CALL _FreeMem POP ESI POP EBX end; class function TObject.InstanceSize: Longint; asm MOV EAX,[EAX].vmtInstanceSize end; constructor TObject.Create; begin end; destructor TObject.Destroy; begin end; procedure TObject.Free; asm TEST EAX,EAX JE @@exit MOV ECX,[EAX] MOV DL,1 CALL dword ptr [ECX].vmtDestroy @@exit: end; class function TObject.InitInstance(Instance: Pointer): TObject; 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; procedure TObject.CleanupInstance; 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; function InvokeImplGetter(Self: TObject; ImplGetter: Integer): IUnknown; 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; function TObject.GetInterface(const IID: TGUID; out Obj): Boolean; var InterfaceEntry: PInterfaceEntry; begin InterfaceEntry := GetInterfaceEntry(IID); if InterfaceEntry <> nil then begin if InterfaceEntry^.IOffset <> 0 then Pointer(Obj) := Pointer(Integer(Self) + InterfaceEntry^.IOffset) else IUnknown(Obj) := InvokeImplGetter(Self, InterfaceEntry^.ImplGetter); if Pointer(Obj) <> nil then begin if InterfaceEntry^.IOffset <> 0 then IUnknown(Obj)._AddRef; Result := True; end else Result := False; end else begin Pointer(Obj) := nil; Result := False; end; end; class function TObject.GetInterfaceEntry(const IID: TGUID): PInterfaceEntry; 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; class function TObject.GetInterfaceTable: PInterfaceTable; asm MOV EAX,[EAX].vmtIntfTable end; procedure _IsClass; asm { -> EAX left operand (class) } { EDX VMT of right operand } { <- AL left is derived from right } TEST EAX,EAX JE @@exit @@loop: MOV EAX,[EAX] CMP EAX,EDX JE @@success MOV EAX,[EAX].vmtParent TEST EAX,EAX JNE @@loop JMP @@exit @@success: MOV AL,1 @@exit: end; procedure _AsClass; 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; procedure GetDynaMethod; { function GetDynaMethod(vmt: TClass; selector: Smallint) : Pointer; } asm { -> EAX vmt of class } { BX dynamic method index } { <- EBX pointer to routine } { ZF = 0 if found } { trashes: EAX, ECX } PUSH EDI XCHG EAX,EBX JMP @@haveVMT @@outerLoop: MOV EBX,[EBX] @@haveVMT: MOV EDI,[EBX].vmtDynamicTable TEST EDI,EDI JE @@parent MOVZX ECX,word ptr [EDI] PUSH ECX ADD EDI,2 REPNE SCASW JE @@found POP ECX @@parent: MOV EBX,[EBX].vmtParent TEST EBX,EBX JNE @@outerLoop JMP @@exit @@found: POP EAX ADD EAX,EAX SUB EAX,ECX { this will always clear the Z-flag ! } MOV EBX,[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 EBX @@Abstract: POP ECX JMP _AbstractError end; procedure _CallDynaClass; asm PUSH EAX PUSH ECX CALL GetDynaMethod POP ECX POP EAX JE @@Abstract JMP EBX @@Abstract: POP ECX JMP _AbstractError end; procedure _FindDynaInst; asm PUSH EBX MOV EBX,EDX MOV EAX,[EAX] CALL GetDynaMethod MOV EAX,EBX POP EBX JNE @@exit POP ECX JMP _AbstractError @@exit: end; procedure _FindDynaClass; asm PUSH EBX MOV EBX,EDX CALL GetDynaMethod MOV EAX,EBX POP EBX JNE @@exit POP ECX JMP _AbstractError @@exit: end; class function TObject.InheritsFrom(AClass: TClass): Boolean; 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; class function TObject.ClassInfo: Pointer; asm MOV EAX,[EAX].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 EBX MOV BX,[EDX] OR BX,BX JE @@default CMP BX,0C000H JAE @@default PUSH EAX MOV EAX,[EAX] CALL GetDynaMethod POP EAX JE @@default MOV ECX,EBX POP EBX JMP ECX @@default: POP EBX 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; 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; type JmpInstruction = packed record opCode: Byte; distance: Longint; end; TExcDescEntry = record vTable: Pointer; handler: Pointer; end; PExcDesc = ^TExcDesc; TExcDesc = packed record jmp: JmpInstruction; case Integer of 0: (instructions: array [0..0] of Byte); 1{...}: (cnt: Integer; excTab: array [0..0{cnt-1}] of TExcDescEntry); end; 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; PRaiseFrame = ^TRaiseFrame; TRaiseFrame = packed record NextRaise: PRaiseFrame; ExceptAddr: Pointer; ExceptObject: TObject; ExceptionRecord: PExceptionRecord; end; procedure _ClassCreate; 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: 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 POP EBX POP ECX POP EDX RET @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 end; procedure _ClassDestroy; asm MOV EDX,[EAX] CALL dword ptr [EDX].vmtFreeInstance end; procedure _AfterConstruction; asm { -> EAX = pointer to instance } PUSH EAX MOV EDX,[EAX] CALL dword ptr [EDX].vmtAfterConstruction POP EAX end; procedure _BeforeDestruction; 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; { 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. } { tell the debugger that the next raise is a re-raise of the current non-Delphi exception } procedure NotifyReRaise; asm CMP BYTE PTR DebugHook,1 JBE @@1 PUSH 0 PUSH 0 PUSH cContinuable PUSH cDelphiReRaise CALL RaiseException @@1: end; { tell the debugger about the raise of a non-Delphi exception } 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 RaiseException ADD ESP,8 POP EAX @@1: end; { Tell the debugger where the handler for the current exception is located } procedure NotifyExcept; asm PUSH ESP PUSH 1 PUSH cContinuable PUSH cDelphiExcept { our magic exception code } CALL RaiseException ADD ESP,4 POP EAX end; procedure NotifyOnExcept; asm CMP BYTE PTR DebugHook,1 JBE @@1 PUSH EAX PUSH [EBX].TExcDescEntry.handler JMP NotifyExcept @@1: end; 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; { Notify debugger of a finally during an exception unwind } procedure NotifyExceptFinally; asm 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 RaiseException POP ECX POP ECX POP EDX POP EAX @@1: end; { Tell the debugger that the current exception is handled and cleaned up. Also indicate where execution is about to resume. } 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 RaiseException POP EDX @@1: end; { Tell the debugger that there was no handler found for the current execption and we are about to go to the default handler } procedure NotifyUnhandled; asm PUSH EAX PUSH EDX CMP BYTE PTR DebugHook,1 JBE @@1 PUSH ESP PUSH 2 PUSH cContinuable PUSH cDelphiUnhandled CALL RaiseException @@1: POP EDX POP EAX end; procedure _HandleAnyException; 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 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 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 @@CppException: MOV EDX,EAX MOV EAX,[ESP+4] MOV ECX,[EAX].TExceptionRecord.ExceptionAddress @@DelphiException: 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 @@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 RtlUnwind @@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 end; procedure _HandleOnException; 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 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 @@noJIT: MOV EDX,EAX MOV EAX,[ESP+4+4*4] MOV ECX,[EAX].TExceptionRecord.ExceptionAddress JMP @@GoUnwind @@haveObject: 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 @@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 RtlUnwind @@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; procedure _HandleFinally; 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] 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 end; procedure _HandleAutoException; 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 RtlUnwind @@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 MOV EBX,[EAX] CALL [EBX].vmtSafeCallException.Pointer MOV EBX,EAX 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; procedure _RaiseExcept; asm { 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 } 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 RaiseException end; procedure _RaiseAgain; asm { -> [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 end; procedure _DoneExcept; asm { -> [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 end; procedure _TryFinallyExit; asm 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 end; type PInitContext = ^TInitContext; TInitContext = record OuterContext: PInitContext; { saved InitContext } ExcFrame: PExcFrame; { bottom exc handler } 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 } DLLInitState: Byte; ExitProcessTLS: procedure; { Shutdown for TLS } end; var InitContext: TInitContext; procedure RunErrorAt(ErrCode: Integer; ErrorAddr: Pointer); asm MOV [ESP],ErrorAddr JMP _RunError end; 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 CMP BYTE PTR DebugHook,0 JA @@ExecuteHandler LEA EAX,[ESP+4] PUSH EAX CALL UnhandledExceptionFilter CMP EAX,EXCEPTION_CONTINUE_SEARCH JNE @@ExecuteHandler JMP @@exit // MOV EAX,1 // RET @@ExecuteHandler: MOV EAX,[ESP+4] CLD CALL FpuInit MOV EDX,[ESP+8] PUSH 0 PUSH EAX PUSH offset @@returnAddress PUSH EDX CALL RtlUnwind @@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 + $60] 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 MOV [EAX].TExcFrame.desc,offset _ExceptionHandler MOV [EAX].TExcFrame.hEBP,EBP MOV InitContext.ExcFrame,EAX {X} POP EBP end; procedure UnsetExceptionHandler; asm XOR EDX,EDX MOV EAX,InitContext.ExcFrame 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; {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; try while Count > 0 do begin Dec(Count); InitContext.InitCount := Count; P := Table^[Count].FInit; if Assigned(P) then P; 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; while Count > 0 do begin Dec(Count); InitContext.InitCount := Count; P := Table^[Count].FInit; if Assigned(P) then P; 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: procedure; } begin if InitContext.InitTable = nil then exit; Count := InitContext.InitTable^.UnitCount; I := 0; Table := InitContext.InitTable^.UnitInfo; {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 P; end; except FInitUnits; raise; end; X+} InitUnitsProc( Table, I, Count ); end; procedure _PackageLoad(const Table : PackageInfo); var SavedContext: TInitContext; begin SavedContext := InitContext; InitContext.DLLInitState := 0; InitContext.InitTable := Table; InitContext.InitCount := 0; InitContext.OuterContext := @SavedContext; try InitUnits; finally InitContext := SavedContext; end; end; procedure _PackageUnload(const Table : PackageInfo); var SavedContext: TInitContext; begin SavedContext := InitContext; InitContext.DLLInitState := 0; InitContext.InitTable := Table; InitContext.InitCount := Table^.UnitCount; InitContext.OuterContext := @SavedContext; try FInitUnitsProc; finally InitContext := SavedContext; end; end; procedure _StartExe; asm { -> EAX InitTable } { EDX Module } MOV InitContext.InitTable,EAX XOR EAX,EAX MOV InitContext.InitCount,EAX MOV InitContext.Module,EDX MOV EAX,[EDX].TLibModule.Instance MOV MainInstance,EAX {X CALL SetExceptionHandler - moved to SysSfIni.pas } MOV IsLibrary,0 CALL InitUnits; end; 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 CALL SetExceptionHandler {X-- could be moved to SysSfIni.pas but ...} 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 @@noTLSproc CALL dword ptr [ECX+EAX*4] @@noTLSproc: { Call any DllProc } MOV EDX,[ESP+4] TEST EDX,EDX JE @@noDllProc MOV EAX,[EBP+12] CALL EDX @@noDllProc: { 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; procedure _InitResStrings; asm { -> EAX Pointer to init table } { record } { cnt: Integer; } { tab: array [1..cnt] record } { variableAddress: Pointer; } { resStringAddress: Pointer; } { end; } { end; } PUSH EBX PUSH ESI MOV EBX,[EAX] LEA ESI,[EAX+4] @@loop: MOV EAX,[ESI+4] { load resStringAddress } MOV EDX,[ESI] { load variableAddress } CALL LoadResString ADD ESI,8 DEC EBX JNZ @@loop POP ESI POP EBX end; procedure _InitResStringImports; asm { -> EAX Pointer to init table } { record } { cnt: Integer; } { tab: array [1..cnt] record } { variableAddress: Pointer; } { resStringAddress: ^Pointer; } { end; } { end; } PUSH EBX PUSH ESI MOV EBX,[EAX] LEA ESI,[EAX+4] @@loop: MOV EAX,[ESI+4] { load address of import } MOV EDX,[ESI] { load address of variable } MOV EAX,[EAX] { load contents of import } CALL LoadResString ADD ESI,8 DEC EBX JNZ @@loop POP ESI POP EBX 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; } PUSH EBX PUSH ESI MOV EBX,[EAX] LEA ESI,[EAX+4] @@loop: MOV EAX,[ESI+4] { load address of import } MOV EDX,[ESI] { load address of variable } MOV ECX,[ESI+8] { load offset } MOV EAX,[EAX] { load contents of import } ADD EAX,ECX { calc address of variable } MOV [EDX],EAX { store result } ADD ESI,12 DEC EBX JNZ @@loop POP ESI POP EBX end; 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; 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'; asm PUSH EBX MOV EAX,ExitCode MOV EBX,offset runErrMsg + 16 MOV ECX,10 @@digLoop: XOR EDX,EDX DIV ECX ADD DL,'0' MOV [EBX],DL DEC EBX TEST EAX,EAX JNZ @@digLoop MOV EAX,ErrorAddr CALL FindHInstance MOV EDX, ErrorAddr XCHG EAX, EDX SUB EAX, EDX { EAX <=> offset from start of code for HINSTANCE } MOV EBX,offset runErrMsg + 28 @@hdigLoop: MOV EDX,EAX AND EDX,0FH MOV DL,byte ptr dig[EDX] MOV [EBX],DL DEC EBX SHR EAX,4 JNE @@hdigLoop POP EBX end; procedure ExitDll; asm { Restore the InitContext } MOV EDI,offset InitContext MOV EBX,InitContext.DLLSaveEBX MOV EBP,InitContext.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 { Return False if ExitCode <> 0, and set ExitCode to 0 } XOR EAX,EAX XCHG EAX,ExitCode NEG EAX SBB EAX,EAX INC EAX LEAVE RET 12 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 ); end; var CloseInputOutput : procedure; procedure UseInputOutput; begin if not assigned( CloseInputOutput ) then begin CloseInputOutput := DoCloseInputOutput; _Assign( Input, '' ); _Assign( Output, '' ); end; end; // {X}- procedure _Halt0; var P: procedure; begin 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 } FInitUnitsProc; if (InitContext.DLLInitState <= 1) or (ExitCode <> 0) then if InitContext.Module <> nil then with InitContext do begin UnregisterModule(Module); if Module.ResInstance <> Module.Instance then FreeLibrary(Module.ResInstance); end; {X UnsetExceptionHandler; - changed to call of handler } UnsetExceptionHandlerProc; if InitContext.DllInitState = 1 then InitContext.ExitProcessTLS; if InitContext.DllInitState <> 0 then ExitDll; if InitContext.OuterContext = nil then ExitProcess(ExitCode); InitContext := InitContext.OuterContext^ end; asm db 'Portions Copyright (c) 1983,99 Borland',0 end; end; procedure _Halt; asm MOV ExitCode,EAX JMP _Halt0 end; procedure _Run0Error; asm XOR EAX,EAX JMP _RunError end; procedure _RunError; asm POP ErrorAddr JMP _Halt end; procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer); asm CMP AssertErrorProc,0 JE @@1 PUSH [ESP].Pointer CALL AssertErrorProc RET @@1: MOV AL,reAssertionFailed JMP Error end; type PThreadRec = ^TThreadRec; TThreadRec = record Func: TThreadFunc; Parameter: Pointer; end; function ThreadWrapper(Parameter: Pointer): Integer; stdcall; asm CALL FpuInit XOR ECX,ECX PUSH EBP PUSH offset _ExceptionHandler MOV EDX,FS:[ECX] PUSH EDX MOV EAX,Parameter MOV FS:[ECX],ESP MOV ECX,[EAX].TThreadRec.Parameter MOV EDX,[EAX].TThreadRec.Func PUSH ECX PUSH EDX CALL _FreeMem POP EDX POP EAX CALL EDX XOR EDX,EDX POP ECX MOV FS:[EDX],ECX POP ECX POP EBP end; 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; type StrRec = packed record allocSiz: Longint; refCnt: Longint; length: Longint; end; const skew = sizeof(StrRec); rOff = sizeof(StrRec) - sizeof(Longint); { refCnt offset } overHead = sizeof(StrRec) + 1; procedure _LStrClr(var S: AnsiString); 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; procedure _LStrArrayClr{var str: AnsiString; cnt: longint}; 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; { 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: AnsiString; source: AnsiString}; 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; procedure _LStrLAsg{var dest: AnsiString; source: AnsiString}; 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; procedure _NewAnsiString{length: Longint}; asm { -> EAX length } { <- EAX pointer to new string } TEST EAX,EAX JLE @@null PUSH EAX ADD EAX,rOff+1 CALL _GetMem ADD EAX,rOff POP EDX MOV [EAX-skew].StrRec.length,EDX MOV [EAX-skew].StrRec.refCnt,1 MOV byte ptr [EAX+EDX],0 RET @@null: XOR EAX,EAX end; 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; procedure _LStrFromPWCharLen(var Dest: AnsiString; Source: PWideChar; Length: Integer); var DestLen: Integer; Buffer: array[0..2047] of Char; begin if Length <= 0 then begin _LStrClr(Dest); Exit; end; if Length < SizeOf(Buffer) div 2 then begin DestLen := WideCharToMultiByte(0, 0, Source, Length, Buffer, SizeOf(Buffer), nil, nil); if DestLen > 0 then begin _LStrFromPCharLen(Dest, Buffer, DestLen); Exit; end; end; DestLen := WideCharToMultiByte(0, 0, Source, Length, nil, 0, nil, nil); _LStrFromPCharLen(Dest, nil, DestLen); WideCharToMultiByte(0, 0, Source, Length, Pointer(Dest), DestLen, nil, nil); 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{str: AnsiString}: Longint; asm { -> EAX str } TEST EAX,EAX JE @@done MOV EAX,[EAX-skew].StrRec.length; @@done: end; 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 } 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-skew].StrRec.length @@1: DEC EDX JNE @@loop1 CALL _NewAnsiString PUSH EAX MOV ESI,EAX @@loop2: MOV EAX,[ESP+EBX*4+5*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 EDX,EDX JE @@skip DEC [EDX-skew].StrRec.refCnt // EDX = local temp str @@skip: CALL _LStrAsg POP EDX 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; procedure _LStrAddRef{str: AnsiString}; 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; procedure _LStrToPChar{str: AnsiString): PChar}; asm { -> EAX pointer to str } { <- EAX pointer to PChar } TEST EAX,EAX JE @@handle0 RET @@zeroByte: DB 0 @@handle0: MOV EAX,offset @@zeroByte end; procedure UniqueString(var str: string); 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 MOV ECX,[EAX-skew].StrRec.refCnt DEC ECX JL @@skip {X LOCK} DEC [EAX-skew].StrRec.refCnt @@skip: MOV ECX,[EAX-skew].StrRec.length CALL Move MOV EDX,[EBX] POP EBX @@exit: MOV EAX,EDX 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 JE @@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; procedure _Write0LString{ VAR t: Text; s: AnsiString }; asm { -> EAX Pointer to text record } { EDX Pointer to AnsiString } XOR ECX,ECX JMP _WriteLString end; procedure _WriteLString{ VAR t: Text; s: AnsiString; width: Longint }; 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; procedure _ReadLString{var t: Text; var str: 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; procedure WStrError; asm MOV AL,reOutOfMemory JMP Error end; procedure WStrSet(var S: WideString; P: PWideChar); asm MOV ECX,[EAX] MOV [EAX],EDX TEST ECX,ECX JE @@1 PUSH ECX CALL SysFreeString @@1: end; procedure WStrClr; asm JMP _WStrClr end; procedure _WStrClr(var S: WideString); 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; procedure WStrArrayClr; asm JMP _WStrArrayClr; end; procedure _WStrArrayClr(var StrArray; Count: Integer); 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; procedure _WStrAsg(var Dest: WideString; const Source: WideString); 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; procedure _WStrFromPCharLen(var Dest: WideString; Source: PAnsiChar; Length: Integer); var DestLen: Integer; Buffer: array[0..1023] of WideChar; begin if Length <= 0 then begin _WStrClr(Dest); Exit; end; if Length < SizeOf(Buffer) div 2 then begin DestLen := MultiByteToWideChar(0, 0, Source, Length, Buffer, SizeOf(Buffer) div 2); if DestLen > 0 then begin _WStrFromPWCharLen(Dest, Buffer, DestLen); Exit; end; end; DestLen := MultiByteToWideChar(0, 0, Source, Length, nil, 0); _WStrFromPWCharLen(Dest, nil, DestLen); MultiByteToWideChar(0, 0, Source, Length, Pointer(Dest), DestLen); end; procedure _WStrFromPWCharLen(var Dest: WideString; Source: PWideChar; Length: Integer); 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; 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 SourceLen := Length(Source); if SourceLen >= 255 then SourceLen := 255; if SourceLen = 0 then DestLen := 0 else begin DestLen := WideCharToMultiByte(0, 0, Pointer(Source), SourceLen, Buffer, SizeOf(Buffer), nil, nil); 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; asm TEST EAX,EAX JE @@1 RET NOP @@0: DW 0 @@1: MOV EAX,OFFSET @@0 end; function _WStrLen(const S: WideString): Integer; asm { -> EAX Pointer to WideString data } TEST EAX,EAX JE @@1 MOV EAX,[EAX-4] SHR EAX,1 @@1: end; 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 * 2); Move(Pointer(Source)^, NewStr[DestLen], SourceLen * 2); 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 * 2); Move(Pointer(Source2)^, NewStr[Source1Len], Source2Len * 2); 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 _NewWideString(Length: Integer): PWideChar; asm TEST EAX,EAX JE @@1 PUSH EAX PUSH 0 CALL SysAllocStringLen TEST EAX,EAX JE WStrError @@1: 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; procedure _WStrAddRef{var str: WideString}; 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; procedure _InitializeRecord{ p: Pointer; typeInfo: Pointer }; 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 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] CALL _Initialize ADD ESI,8 DEC EDI JG @@loop POP EDI POP ESI POP EBX end; const tkLString = 10; tkWString = 11; tkVariant = 12; tkArray = 13; tkRecord = 14; tkInterface = 15; tkDynArray = 17; procedure _InitializeArray{ p: Pointer; typeInfo: Pointer; elemCount: Longint}; asm { -> EAX pointer to data to be initialized } { EDX pointer to type info describing data } { ECX number of elements of that type } 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] 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] MOV EAX,EBX ADD EBX,[ESI+EBP+2] MOV ECX,[ESI+EBP+2+4] 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 end; procedure _Initialize{ p: Pointer; typeInfo: Pointer}; asm MOV ECX,1 JMP _InitializeArray end; procedure _FinalizeRecord{ p: Pointer; typeInfo: Pointer }; 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] CALL _Finalize ADD ESI,8 DEC EDI JG @@loop MOV EAX,EBX POP EDI POP ESI POP EBX end; procedure _FinalizeArray{ p: Pointer; typeInfo: Pointer; elemCount: Longint}; asm { -> EAX pointer to data to be finalized } { EDX pointer to type info describing data } { ECX number of elements of that type } 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 POP EDI POP ESI POP EBX POP EAX MOV AL,reInvalidPtr 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 CALL [WStrClrProc] JMP @@exit @@WStringArray: MOV EDX,ECX //CALL _WStrArrayClr CALL [WStrArrayClrProc] JMP @@exit @@Variant: MOV EAX,EBX ADD EBX,16 //CALL _VarClr CALL [VarClrProc] 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 @@exit: POP EDI POP ESI POP EBX POP EAX @@zerolength: end; procedure _Finalize{ p: Pointer; typeInfo: Pointer}; asm MOV ECX,1 JMP _FinalizeArray end; 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] CALL _AddRef ADD ESI,8 DEC EDI JG @@loop POP EDI POP ESI POP EBX end; procedure DummyProc; begin 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 } 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 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 CALL [VarAddRefProc] 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; procedure _New{ size: Longint; typeInfo: Pointer}; 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; procedure _Dispose{ p: Pointer; typeInfo: Pointer}; asm { -> EAX Pointer to object to be disposed } { EDX Pointer to type info } PUSH EAX CALL _Finalize POP EAX CALL _FreeMem end; { ----------------------------------------------------- } { 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); var SourceLen: Integer; begin SourceLen := 0; while Source[SourceLen] <> #0 do Inc(SourceLen); WideCharLenToStrVar(Source, SourceLen, Dest); end; procedure WideCharLenToStrVar(Source: PWideChar; SourceLen: Integer; var Dest: string); var DestLen: Integer; Buffer: array[0..2047] of Char; begin if SourceLen = 0 then Dest := '' else if SourceLen < SizeOf(Buffer) div 2 then SetString(Dest, Buffer, WideCharToMultiByte(0, 0, Source, SourceLen, Buffer, SizeOf(Buffer), nil, nil)) else begin DestLen := WideCharToMultiByte(0, 0, Source, SourceLen, nil, 0, nil, nil); SetString(Dest, nil, DestLen); WideCharToMultiByte(0, 0, Source, SourceLen, Pointer(Dest), DestLen, nil, nil); end; end; function StringToWideChar(const Source: string; Dest: PWideChar; DestSize: Integer): PWideChar; begin Dest[MultiByteToWideChar(0, 0, PChar(Source), Length(Source), Dest, DestSize - 1)] := #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, SysStringLen(WideString(Pointer(Source))), Dest); end; function StringToOleStr(const Source: string): PWideChar; var SourceLen, ResultLen: Integer; Buffer: array[0..1023] of WideChar; begin SourceLen := Length(Source); if Length(Source) < SizeOf(Buffer) div 2 then Result := SysAllocStringLen(Buffer, MultiByteToWideChar(0, 0, PChar(Source), SourceLen, Buffer, SizeOf(Buffer) div 2)) else begin ResultLen := MultiByteToWideChar(0, 0, Pointer(Source), SourceLen, nil, 0); Result := SysAllocStringLen(nil, ResultLen); MultiByteToWideChar(0, 0, Pointer(Source), SourceLen, Result, ResultLen); end; end; { ----------------------------------------------------- } { Variant support } { ----------------------------------------------------- } type TBaseType = (btErr, btNul, btInt, btFlt, btCur, btStr, btBol, btDat); const varLast = varByte; const BaseTypeMap: array[0..varLast] of TBaseType = ( btErr, { varEmpty } btNul, { varNull } btInt, { varSmallint } btInt, { varInteger } btFlt, { varSingle } btFlt, { varDouble } btCur, { varCurrency } btDat, { varDate } btStr, { varOleStr } btErr, { varDispatch } btErr, { varError } btBol, { varBoolean } btErr, { varVariant } btErr, { varUnknown } btErr, { vt_decimal } btErr, { undefined } btErr, { vt_i1 } btInt); { varByte } const OpTypeMap: array[TBaseType, TBaseType] of TBaseType = ( (btErr, btErr, btErr, btErr, btErr, btErr, btErr, btErr), (btErr, btNul, btNul, btNul, btNul, btNul, btNul, btNul), (btErr, btNul, btInt, btFlt, btCur, btFlt, btInt, btDat), (btErr, btNul, btFlt, btFlt, btCur, btFlt, btFlt, btDat), (btErr, btNul, btCur, btCur, btCur, btCur, btCur, btDat), (btErr, btNul, btFlt, btFlt, btCur, btStr, btBol, btDat), (btErr, btNul, btInt, btFlt, btCur, btBol, btBol, btDat), (btErr, btNul, btDat, btDat, btDat, btDat, btDat, btDat)); const C10000: Single = 10000; const opAdd = 0; opSub = 1; opMul = 2; opDvd = 3; opDiv = 4; opMod = 5; opShl = 6; opShr = 7; opAnd = 8; opOr = 9; opXor = 10; procedure _DispInvoke; asm { -> [ESP+4] Pointer to result or nil } { [ESP+8] Pointer to variant } { [ESP+12] Pointer to call descriptor } { [ESP+16] Additional parameters, if any } JMP VarDispProc end; procedure _DispInvokeError; asm MOV AL,reVarDispatch JMP Error end; procedure VarCastError; asm MOV AL,reVarTypeCast JMP Error end; procedure VarInvalidOp; asm MOV AL,reVarInvalidOp JMP Error end; procedure _VarClear(var V : Variant); asm XOR EDX,EDX MOV DX,[EAX].TVarData.VType TEST EDX,varByRef JNE @@2 CMP EDX,varOleStr JB @@2 CMP EDX,varString JE @@1 CMP EDX,varAny JNE @@3 JMP [ClearAnyProc] @@1: MOV [EAX].TVarData.VType,varEmpty ADD EAX,OFFSET TVarData.VString JMP _LStrClr @@2: MOV [EAX].TVarData.VType,varEmpty RET @@3: PUSH EAX CALL VariantClear end; procedure _VarCopy(var Dest : Variant; const Source: Variant); asm CMP EAX,EDX JE @@9 CMP [EAX].TVarData.VType,varOleStr JB @@3 PUSH EAX PUSH EDX CMP [EAX].TVarData.VType,varString JE @@1 CMP [EAX].TVarData.VType,varAny JE @@0 PUSH EAX CALL VariantClear JMP @@2 @@0: CALL [ClearAnyProc] JMP @@2 @@1: ADD EAX,OFFSET TVarData.VString CALL _LStrClr @@2: POP EDX POP EAX @@3: CMP [EDX].TVarData.VType,varOleStr JAE @@5 @@4: MOV ECX,[EDX] MOV [EAX],ECX MOV ECX,[EDX+8] MOV [EAX+8],ECX MOV ECX,[EDX+12] MOV [EAX+12],ECX RET @@5: CMP [EDX].TVarData.VType,varString JE @@6 CMP [EDX].TVarData.VType,varAny JNE @@8 PUSH EAX CALL @@4 POP EAX JMP [RefAnyProc] @@6: MOV EDX,[EDX].TVarData.VString OR EDX,EDX JE @@7 MOV ECX,[EDX-skew].StrRec.refCnt INC ECX JLE @@7 {X LOCK} INC [EDX-skew].StrRec.refCnt @@7: MOV [EAX].TVarData.VType,varString MOV [EAX].TVarData.VString,EDX RET @@8: MOV [EAX].TVarData.VType,varEmpty PUSH EDX PUSH EAX CALL VariantCopyInd OR EAX,EAX JNE VarInvalidOp @@9: end; procedure VarCopyNoInd(var Dest: Variant; const Source: Variant); asm CMP EAX,EDX JE @@9 CMP [EAX].TVarData.VType,varOleStr JB @@3 PUSH EAX PUSH EDX CMP [EAX].TVarData.VType,varString JE @@1 CMP [EAX].TVarData.VType,varAny JE @@0 PUSH EAX CALL VariantClear JMP @@2 @@0: CALL [ClearAnyProc] JMP @@2 @@1: ADD EAX,OFFSET TVarData.VString CALL _LStrClr @@2: POP EDX POP EAX @@3: CMP [EDX].TVarData.VType,varOleStr JAE @@5 @@4: MOV ECX,[EDX] MOV [EAX],ECX MOV ECX,[EDX+8] MOV [EAX+8],ECX MOV ECX,[EDX+12] MOV [EAX+12],ECX RET @@5: CMP [EDX].TVarData.VType,varString JNE @@6 CMP [EDX].TVarData.VType,varAny JNE @@8 CALL @@4 JMP [RefAnyProc] @@6: MOV EDX,[EDX].TVarData.VString OR EDX,EDX JE @@7 MOV ECX,[EDX-skew].StrRec.refCnt INC ECX JLE @@7 {X LOCK} INC [EDX-skew].StrRec.refCnt @@7: MOV [EAX].TVarData.VType,varString MOV [EAX].TVarData.VString,EDX RET @@8: MOV [EAX].TVarData.VType,varEmpty PUSH EDX PUSH EAX CALL VariantCopy @@9: end; type TAnyProc = procedure (var V: Variant); procedure VarChangeType(var Dest: Variant; const Source: Variant; DestType: Word); forward; procedure AnyChangeType(var Dest: Variant; Source: Variant; DestType: Word); begin TAnyProc(ChangeAnyProc)(Source); VarChangeType(Dest, Source, DestType); end; procedure VarChangeType(var Dest: Variant; const Source: Variant; DestType: Word); type TVarMem = array[0..3] of Integer; function ChangeSourceAny(var Dest: Variant; const Source: Variant; DestType: Word): Boolean; begin Result := False; if TVarData(Source).VType = varAny then begin AnyChangeType(Dest, Source, DestType); Result := True; end; end; var Temp: TVarData; begin case TVarData(Dest).VType of varString: begin if not ChangeSourceAny(Dest, Source, DestType) then begin Temp.VType := varEmpty; if VariantChangeTypeEx(Variant(Temp), Source, $400, 0, DestType) <> 0 then VarCastError; _VarClear(Dest); TVarMem(Dest)[0] := TVarMem(Temp)[0]; TVarMem(Dest)[2] := TVarMem(Temp)[2]; TVarMem(Dest)[3] := TVarMem(Temp)[3]; end; end; varAny: AnyChangeType(Dest, Source, DestType); else if not ChangeSourceAny(Dest, Source, DestType) then if VariantChangeTypeEx(Dest, Source, $400, 0, DestType) <> 0 then VarCastError; end; end; procedure VarOleStrToString(var Dest: Variant; const Source: Variant); var StringPtr: Pointer; begin StringPtr := nil; OleStrToStrVar(TVarData(Source).VOleStr, string(StringPtr)); _VarClear(Dest); TVarData(Dest).VType := varString; TVarData(Dest).VString := StringPtr; end; procedure VarStringToOleStr(var Dest: Variant; const Source: Variant); var OleStrPtr: PWideChar; begin OleStrPtr := StringToOleStr(string(TVarData(Source).VString)); _VarClear(Dest); TVarData(Dest).VType := varOleStr; TVarData(Dest).VOleStr := OleStrPtr; end; procedure _VarCast(var Dest : Variant; const Source: Variant; VarType: Integer); var SourceType, DestType: Word; Temp: TVarData; begin SourceType := TVarData(Source).VType; DestType := Word(VarType); if SourceType = DestType then _VarCopy(Dest, Source) else if SourceType = varString then if DestType = varOleStr then VarStringToOleStr(Variant(Dest), Source) else begin Temp.VType := varEmpty; VarStringToOleStr(Variant(Temp), Source); try VarChangeType(Variant(Dest), Variant(Temp), DestType); finally _VarClear(PVariant(@Temp)^); end; end else if (DestType = varString) and (SourceType <> varAny) then if SourceType = varOleStr then VarOleStrToString(Variant(Dest), Source) else begin Temp.VType := varEmpty; VarChangeType(Variant(Temp), Source, varOleStr); try VarOleStrToString(Variant(Dest), Variant(Temp)); finally _VarClear(Variant(Temp)); end; end else VarChangeType(Variant(Dest), Source, DestType); end; (* VarCast when the destination is OleVariant *) procedure _VarCastOle(var Dest : Variant; const Source: Variant; VarType: Integer); begin if (VarType = varString) or (VarType = varAny) then VarCastError else _VarCast(Dest, Source, VarType); end; procedure _VarToInt; asm XOR EDX,EDX MOV DX,[EAX].TVarData.VType CMP EDX,varInteger JE @@0 CMP EDX,varSmallint JE @@1 CMP EDX,varByte JE @@2 CMP EDX,varDouble JE @@5 CMP EDX,varSingle JE @@4 CMP EDX,varCurrency JE @@3 SUB ESP,16 MOV [ESP].TVarData.VType,varEmpty MOV EDX,EAX MOV EAX,ESP MOV ECX,varInteger CALL _VarCast MOV EAX,[ESP].TVarData.VInteger ADD ESP,16 RET @@0: MOV EAX,[EAX].TVarData.VInteger RET @@1: MOVSX EAX,[EAX].TVarData.VSmallint RET @@2: MOVZX EAX,[EAX].TVarData.VByte RET @@3: FILD [EAX].TVarData.VCurrency FDIV C10000 JMP @@6 @@4: FLD [EAX].TVarData.VSingle JMP @@6 @@5: FLD [EAX].TVarData.VDouble @@6: PUSH EAX FISTP DWORD PTR [ESP] FWAIT POP EAX end; procedure _VarToBool; asm CMP [EAX].TVarData.VType,varBoolean JE @@1 SUB ESP,16 MOV [ESP].TVarData.VType,varEmpty MOV EDX,EAX MOV EAX,ESP MOV ECX,varBoolean CALL _VarCast MOV AX,[ESP].TVarData.VBoolean ADD ESP,16 JMP @@2 @@1: MOV AX,[EAX].TVarData.VBoolean @@2: NEG AX SBB EAX,EAX NEG EAX end; procedure _VarToReal; asm XOR EDX,EDX MOV DX,[EAX].TVarData.VType CMP EDX,varDouble JE @@1 CMP EDX,varSingle JE @@2 CMP EDX,varCurrency JE @@3 CMP EDX,varInteger JE @@4 CMP EDX,varSmallint JE @@5 CMP EDX,varDate JE @@1 SUB ESP,16 MOV [ESP].TVarData.VType,varEmpty MOV EDX,EAX MOV EAX,ESP MOV ECX,varDouble CALL _VarCast FLD [ESP].TVarData.VDouble ADD ESP,16 RET @@1: FLD [EAX].TVarData.VDouble RET @@2: FLD [EAX].TVarData.VSingle RET @@3: FILD [EAX].TVarData.VCurrency FDIV C10000 RET @@4: FILD [EAX].TVarData.VInteger RET @@5: FILD [EAX].TVarData.VSmallint end; procedure _VarToCurr; asm XOR EDX,EDX MOV DX,[EAX].TVarData.VType CMP EDX,varCurrency JE @@1 CMP EDX,varDouble JE @@2 CMP EDX,varSingle JE @@3 CMP EDX,varInteger JE @@4 CMP EDX,varSmallint JE @@5 SUB ESP,16 MOV [ESP].TVarData.VType,varEmpty MOV EDX,EAX MOV EAX,ESP MOV ECX,varCurrency CALL _VarCast FILD [ESP].TVarData.VCurrency ADD ESP,16 RET @@1: FILD [EAX].TVarData.VCurrency RET @@2: FLD [EAX].TVarData.VDouble JMP @@6 @@3: FLD [EAX].TVarData.VSingle JMP @@6 @@4: FILD [EAX].TVarData.VInteger JMP @@6 @@5: FILD [EAX].TVarData.VSmallint @@6: FMUL C10000 end; procedure _VarToPStr(var S; const V: Variant); var Temp: string; begin _VarToLStr(Temp, V); ShortString(S) := Temp; end; procedure _VarToLStr(var S: string; const V: Variant); asm { -> EAX: destination string } { EDX: source variant } { <- none } CMP [EDX].TVarData.VType,varString JNE @@1 MOV EDX,[EDX].TVarData.VString JMP _LStrAsg @@1: PUSH EBX MOV EBX,EAX SUB ESP,16 MOV [ESP].TVarData.VType,varEmpty MOV EAX,ESP MOV ECX,varString CALL _VarCast MOV EAX,EBX CALL _LStrClr MOV EAX,[ESP].TVarData.VString MOV [EBX],EAX ADD ESP,16 POP EBX end; procedure _VarToWStr(var S: WideString; const V: Variant); asm CMP [EDX].TVarData.VType,varOleStr JNE @@1 MOV EDX,[EDX].TVarData.VOleStr JMP _WStrAsg @@1: PUSH EBX MOV EBX,EAX SUB ESP,16 MOV [ESP].TVarData.VType,varEmpty MOV EAX,ESP MOV ECX,varOleStr CALL _VarCast MOV EAX,EBX MOV EDX,[ESP].TVarData.VOleStr CALL WStrSet ADD ESP,16 POP EBX end; procedure AnyToIntf(var Unknown: IUnknown; V: Variant); begin TAnyProc(ChangeAnyProc)(V); if TVarData(V).VType <> varUnknown then VarCastError; Unknown := IUnknown(TVarData(V).VUnknown); end; procedure _VarToIntf(var Unknown: IUnknown; const V: Variant); asm CMP [EDX].TVarData.VType,varEmpty JE _IntfClear CMP [EDX].TVarData.VType,varUnknown JE @@2 CMP [EDX].TVarData.VType,varDispatch JE @@2 CMP [EDX].TVarData.VType,varUnknown+varByRef JE @@1 CMP [EDX].TVarData.VType,varDispatch+varByRef JE @@1 CMP [EDX].TVarData.VType,varAny JNE VarCastError JMP AnyToIntf @@0: CALL _VarClear ADD ESP,16 JMP VarCastError @@1: MOV EDX,[EDX].TVarData.VPointer MOV EDX,[EDX] JMP _IntfCopy @@2: MOV EDX,[EDX].TVarData.VUnknown JMP _IntfCopy end; procedure _VarToDisp(var Dispatch: IDispatch; const V: Variant); asm CMP [EDX].TVarData.VType,varEmpty JE _IntfClear CMP [EDX].TVarData.VType,varDispatch JE @@1 CMP [EDX].TVarData.VType,varDispatch+varByRef JNE VarCastError MOV EDX,[EDX].TVarData.VPointer MOV EDX,[EDX] JMP _IntfCopy @@1: MOV EDX,[EDX].TVarData.VDispatch JMP _IntfCopy end; procedure _VarToDynArray(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer); asm CALL DynArrayFromVariant OR EAX, EAX JNZ @@1 JMP VarCastError @@1: end; procedure _VarFromInt; asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX PUSH EDX CALL _VarClear POP EDX POP EAX @@1: MOV [EAX].TVarData.VType,varInteger MOV [EAX].TVarData.VInteger,EDX end; procedure _VarFromBool; asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX PUSH EDX CALL _VarClear POP EDX POP EAX @@1: MOV [EAX].TVarData.VType,varBoolean NEG DL SBB EDX,EDX MOV [EAX].TVarData.VBoolean,DX end; procedure _VarFromReal; asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX CALL _VarClear POP EAX @@1: MOV [EAX].TVarData.VType,varDouble FSTP [EAX].TVarData.VDouble FWAIT end; procedure _VarFromTDateTime; asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX CALL _VarClear POP EAX @@1: MOV [EAX].TVarData.VType,varDate FSTP [EAX].TVarData.VDouble FWAIT end; procedure _VarFromCurr; asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX CALL _VarClear POP EAX @@1: MOV [EAX].TVarData.VType,varCurrency FISTP [EAX].TVarData.VCurrency FWAIT end; procedure _VarFromPStr(var V: Variant; const Value: ShortString); begin _VarFromLStr(V, Value); end; procedure _VarFromLStr(var V: Variant; const Value: string); asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX PUSH EDX CALL _VarClear POP EDX POP EAX @@1: TEST EDX,EDX JE @@3 MOV ECX,[EDX-skew].StrRec.refCnt INC ECX JLE @@2 {X LOCK} INC [EDX-skew].StrRec.refCnt JMP @@3 @@2: PUSH EAX PUSH EDX MOV EAX,[EDX-skew].StrRec.length CALL _NewAnsiString MOV EDX,EAX POP EAX PUSH EDX MOV ECX,[EDX-skew].StrRec.length CALL Move POP EDX POP EAX @@3: MOV [EAX].TVarData.VType,varString MOV [EAX].TVarData.VString,EDX end; procedure _VarFromWStr(var V: Variant; const Value: WideString); asm PUSH EAX CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EDX CALL _VarClear POP EDX @@1: XOR EAX,EAX TEST EDX,EDX JE @@2 MOV EAX,[EDX-4] SHR EAX,1 JE @@2 PUSH EAX PUSH EDX CALL SysAllocStringLen TEST EAX,EAX JE WStrError @@2: POP EDX MOV [EDX].TVarData.VType,varOleStr MOV [EDX].TVarData.VOleStr,EAX end; procedure _VarFromIntf(var V: Variant; const Value: IUnknown); asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX PUSH EDX CALL _VarClear POP EDX POP EAX @@1: MOV [EAX].TVarData.VType,varUnknown MOV [EAX].TVarData.VUnknown,EDX TEST EDX,EDX JE @@2 PUSH EDX MOV EAX,[EDX] CALL [EAX].vmtAddRef.Pointer @@2: end; procedure _VarFromDisp(var V: Variant; const Value: IDispatch); asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX PUSH EDX CALL _VarClear POP EDX POP EAX @@1: MOV [EAX].TVarData.VType,varDispatch MOV [EAX].TVarData.VDispatch,EDX TEST EDX,EDX JE @@2 PUSH EDX MOV EAX,[EDX] CALL [EAX].vmtAddRef.Pointer @@2: end; procedure _VarFromDynArray(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer); asm PUSH EAX CALL DynArrayToVariant POP EAX CMP [EAX].TVarData.VType,varEmpty JNE @@1 JMP VarCastError @@1: end; procedure _OleVarFromPStr(var V: OleVariant; const Value: ShortString); begin _OleVarFromLStr(V, Value); end; procedure _OleVarFromLStr(var V: OleVariant; const Value: string); asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX PUSH EDX CALL _VarClear POP EDX POP EAX @@1: MOV [EAX].TVarData.VType,varOleStr ADD EAX,TVarData.VOleStr XOR ECX,ECX MOV [EAX],ECX JMP _WStrFromLStr end; procedure OleVarFromAny(var V: OleVariant; Value: Variant); begin TAnyProc(ChangeAnyProc)(Value); V := Value; end; procedure _OleVarFromVar(var V: OleVariant; const Value: Variant); asm CMP [EDX].TVarData.VType,varAny JE OleVarFromAny CMP [EDX].TVarData.VType,varString JNE _VarCopy CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH EAX PUSH EDX CALL _VarClear POP EDX POP EAX @@1: MOV [EAX].TVarData.VType,varOleStr ADD EAX,TVarData.VOleStr ADD EDX,TVarData.VString XOR ECX,ECX MOV EDX,[EDX] MOV [EAX],ECX JMP _WStrFromLStr @@2: end; procedure VarStrCat(var Dest: Variant; const Source: Variant); begin if TVarData(Dest).VType = varString then Dest := string(Dest) + string(Source) else Dest := WideString(Dest) + WideString(Source); end; procedure VarOp(var Dest: Variant; const Source: Variant; OpCode: Integer); forward; procedure AnyOp(var Dest: Variant; Source: Variant; OpCode: Integer); begin if TVarData(Dest).VType = varAny then TAnyProc(ChangeAnyProc)(Dest); if TVarData(Source).VType = varAny then TAnyProc(ChangeAnyProc)(Source); VarOp(Dest, Source, OpCode); end; procedure VarOp(var Dest: Variant; const Source: Variant; OpCode: Integer); asm PUSH EBX PUSH ESI PUSH EDI MOV EDI,EAX MOV ESI,EDX MOV EBX,ECX MOV EAX,[EDI].TVarData.VType.Integer MOV EDX,[ESI].TVarData.VType.Integer AND EAX,varTypeMask AND EDX,varTypeMask CMP EAX,varLast JBE @@1 CMP EAX,varString JNE @@4 MOV EAX,varOleStr @@1: CMP EDX,varLast JBE @@2 CMP EDX,varString JNE @@3 MOV EDX,varOleStr @@2: MOV AL,BaseTypeMap.Byte[EAX] MOV DL,BaseTypeMap.Byte[EDX] MOVZX ECX,OpTypeMap.Byte[EAX*8+EDX] CALL @VarOpTable.Pointer[ECX*4] POP EDI POP ESI POP EBX RET @@3: MOV EAX,EDX @@4: CMP EAX,varAny JNE @InvalidOp POP EDI POP ESI POP EBX JMP AnyOp @VarOpTable: DD @VarOpError DD @VarOpNull DD @VarOpInteger DD @VarOpReal DD @VarOpCurr DD @VarOpString DD @VarOpBoolean DD @VarOpDate @VarOpError: POP EAX @InvalidOp: POP EDI POP ESI POP EBX JMP VarInvalidOp @VarOpNull: MOV EAX,EDI CALL _VarClear MOV [EDI].TVarData.VType,varNull RET @VarOpInteger: CMP BL,opDvd JE @RealOp @IntegerOp: MOV EAX,ESI CALL _VarToInt PUSH EAX MOV EAX,EDI CALL _VarToInt POP EDX CALL @IntegerOpTable.Pointer[EBX*4] MOV EDX,EAX MOV EAX,EDI JMP _VarFromInt @IntegerOpTable: DD @IntegerAdd DD @IntegerSub DD @IntegerMul DD 0 DD @IntegerDiv DD @IntegerMod DD @IntegerShl DD @IntegerShr DD @IntegerAnd DD @IntegerOr DD @IntegerXor @IntegerAdd: ADD EAX,EDX JO @IntToRealOp RET @IntegerSub: SUB EAX,EDX JO @IntToRealOp RET @IntegerMul: IMUL EDX JO @IntToRealOp RET @IntegerDiv: MOV ECX,EDX CDQ IDIV ECX RET @IntegerMod: MOV ECX,EDX CDQ IDIV ECX MOV EAX,EDX RET @IntegerShl: MOV ECX,EDX SHL EAX,CL RET @IntegerShr: MOV ECX,EDX SHR EAX,CL RET @IntegerAnd: AND EAX,EDX RET @IntegerOr: OR EAX,EDX RET @IntegerXor: XOR EAX,EDX RET @IntToRealOp: POP EAX JMP @RealOp @VarOpReal: CMP BL,opDiv JAE @IntegerOp @RealOp: MOV EAX,ESI CALL _VarToReal SUB ESP,12 FSTP TBYTE PTR [ESP] MOV EAX,EDI CALL _VarToReal FLD TBYTE PTR [ESP] ADD ESP,12 CALL @RealOpTable.Pointer[EBX*4] @RealResult: MOV EAX,EDI JMP _VarFromReal @VarOpCurr: CMP BL,opDiv JAE @IntegerOp CMP BL,opMul JAE @CurrMulDvd MOV EAX,ESI CALL _VarToCurr SUB ESP,12 FSTP TBYTE PTR [ESP] MOV EAX,EDI CALL _VarToCurr FLD TBYTE PTR [ESP] ADD ESP,12 CALL @RealOpTable.Pointer[EBX*4] @CurrResult: MOV EAX,EDI JMP _VarFromCurr @CurrMulDvd: CMP DL,btCur JE @CurrOpCurr MOV EAX,ESI CALL _VarToReal FILD [EDI].TVarData.VCurrency FXCH CALL @RealOpTable.Pointer[EBX*4] JMP @CurrResult @CurrOpCurr: CMP BL,opDvd JE @CurrDvdCurr CMP AL,btCur JE @CurrMulCurr MOV EAX,EDI CALL _VarToReal FILD [ESI].TVarData.VCurrency FMUL JMP @CurrResult @CurrMulCurr: FILD [EDI].TVarData.VCurrency FILD [ESI].TVarData.VCurrency FMUL FDIV C10000 JMP @CurrResult @CurrDvdCurr: MOV EAX,EDI CALL _VarToCurr FILD [ESI].TVarData.VCurrency FDIV JMP @RealResult @RealOpTable: DD @RealAdd DD @RealSub DD @RealMul DD @RealDvd @RealAdd: FADD RET @RealSub: FSUB RET @RealMul: FMUL RET @RealDvd: FDIV RET @VarOpString: CMP BL,opAdd JNE @VarOpReal MOV EAX,EDI MOV EDX,ESI JMP VarStrCat @VarOpBoolean: CMP BL,opAnd JB @VarOpReal MOV EAX,ESI CALL _VarToBool PUSH EAX MOV EAX,EDI CALL _VarToBool POP EDX CALL @IntegerOpTable.Pointer[EBX*4] MOV EDX,EAX MOV EAX,EDI JMP _VarFromBool @VarOpDate: CMP BL,opSub JA @VarOpReal JB @DateOp MOV AH,DL CMP AX,btDat+btDat*256 JE @RealOp @DateOp: CALL @RealOp MOV [EDI].TVarData.VType,varDate RET end; procedure _VarAdd; asm MOV ECX,opAdd JMP VarOp end; procedure _VarSub; asm MOV ECX,opSub JMP VarOp end; procedure _VarMul; asm MOV ECX,opMul JMP VarOp end; procedure _VarDiv; asm MOV ECX,opDiv JMP VarOp end; procedure _VarMod; asm MOV ECX,opMod JMP VarOp end; procedure _VarAnd; asm MOV ECX,opAnd JMP VarOp end; procedure _VarOr; asm MOV ECX,opOr JMP VarOp end; procedure _VarXor; asm MOV ECX,opXor JMP VarOp end; procedure _VarShl; asm MOV ECX,opShl JMP VarOp end; procedure _VarShr; asm MOV ECX,opShr JMP VarOp end; procedure _VarRDiv; asm MOV ECX,opDvd JMP VarOp end; function VarCompareString(const S1, S2: string): Integer; asm PUSH ESI PUSH EDI MOV ESI,EAX MOV EDI,EDX OR EAX,EAX JE @@1 MOV EAX,[EAX-4] @@1: OR EDX,EDX JE @@2 MOV EDX,[EDX-4] @@2: MOV ECX,EAX CMP ECX,EDX JBE @@3 MOV ECX,EDX @@3: CMP ECX,ECX REPE CMPSB JE @@4 MOVZX EAX,BYTE PTR [ESI-1] MOVZX EDX,BYTE PTR [EDI-1] @@4: SUB EAX,EDX POP EDI POP ESI end; function VarCmpStr(const V1, V2: Variant): Integer; begin Result := VarCompareString(V1, V2); end; function AnyCmp(var Dest: Variant; const Source: Variant): Integer; var Temp: Variant; P: ^Variant; begin asm PUSH Dest end; P := @Source; if TVarData(Dest).VType = varAny then TAnyProc(ChangeAnyProc)(Dest); if TVarData(Source).VType = varAny then begin Temp := Source; TAnyProc(ChangeAnyProc)(Temp); P := @Temp; end; asm MOV EDX,P POP EAX CALL _VarCmp PUSHF POP EAX MOV Result,EAX end; end; procedure _VarCmp; asm PUSH ESI PUSH EDI MOV EDI,EAX MOV ESI,EDX MOV EAX,[EDI].TVarData.VType.Integer MOV EDX,[ESI].TVarData.VType.Integer AND EAX,varTypeMask AND EDX,varTypeMask CMP EAX,varLast JBE @@1 CMP EAX,varString JNE @@4 MOV EAX,varOleStr @@1: CMP EDX,varLast JBE @@2 CMP EDX,varString JNE @@3 MOV EDX,varOleStr @@2: MOV AL,BaseTypeMap.Byte[EAX] MOV DL,BaseTypeMap.Byte[EDX] MOVZX ECX,OpTypeMap.Byte[EAX*8+EDX] JMP @VarCmpTable.Pointer[ECX*4] @@3: MOV EAX,EDX @@4: CMP EAX,varAny JNE @VarCmpError POP EDI POP ESI CALL AnyCmp PUSH EAX POPF RET @VarCmpTable: DD @VarCmpError DD @VarCmpNull DD @VarCmpInteger DD @VarCmpReal DD @VarCmpCurr DD @VarCmpString DD @VarCmpBoolean DD @VarCmpDate @VarCmpError: POP EDI POP ESI JMP VarInvalidOp @VarCmpNull: CMP AL,DL JMP @Exit @VarCmpInteger: MOV EAX,ESI CALL _VarToInt XCHG EAX,EDI CALL _VarToInt CMP EAX,EDI JMP @Exit @VarCmpReal: @VarCmpDate: MOV EAX,EDI CALL _VarToReal SUB ESP,12 FSTP TBYTE PTR [ESP] MOV EAX,ESI CALL _VarToReal FLD TBYTE PTR [ESP] ADD ESP,12 @RealCmp: FCOMPP FNSTSW AX MOV AL,AH { Move CF into SF } AND AX,4001H ROR AL,1 OR AH,AL SAHF JMP @Exit @VarCmpCurr: MOV EAX,EDI CALL _VarToCurr SUB ESP,12 FSTP TBYTE PTR [ESP] MOV EAX,ESI CALL _VarToCurr FLD TBYTE PTR [ESP] ADD ESP,12 JMP @RealCmp @VarCmpString: MOV EAX,EDI MOV EDX,ESI CALL VarCmpStr CMP EAX,0 JMP @Exit @VarCmpBoolean: MOV EAX,ESI CALL _VarToBool XCHG EAX,EDI CALL _VarToBool MOV EDX,EDI CMP AL,DL @Exit: POP EDI POP ESI end; procedure _VarNeg; asm MOV EDX,[EAX].TVarData.VType.Integer AND EDX,varTypeMask CMP EDX,varLast JBE @@1 CMP EDX,varString JNE @VarNegError MOV EDX,varOleStr @@1: MOV DL,BaseTypeMap.Byte[EDX] JMP @VarNegTable.Pointer[EDX*4] @@2: CMP EAX,varAny JNE @VarNegError PUSH EAX CALL [ChangeAnyProc] POP EAX JMP _VarNeg @VarNegTable: DD @VarNegError DD @VarNegNull DD @VarNegInteger DD @VarNegReal DD @VarNegCurr DD @VarNegReal DD @VarNegInteger DD @VarNegDate @VarNegError: JMP VarInvalidOp @VarNegNull: RET @VarNegInteger: PUSH EAX CALL _VarToInt NEG EAX MOV EDX,EAX POP EAX JMP _VarFromInt @VarNegReal: PUSH EAX CALL _VarToReal FCHS POP EAX JMP _VarFromReal @VarNegCurr: FILD [EAX].TVarData.VCurrency FCHS FISTP [EAX].TVarData.VCurrency FWAIT RET @VarNegDate: FLD [EAX].TVarData.VDate FCHS FSTP [EAX].TVarData.VDate FWAIT end; procedure _VarNot; asm MOV EDX,[EAX].TVarData.VType.Integer AND EDX,varTypeMask JE @@2 CMP EDX,varBoolean JE @@3 CMP EDX,varNull JE @@4 CMP EDX,varLast JBE @@1 CMP EDX,varString JE @@1 CMP EAX,varAny JNE @@2 PUSH EAX CALL [ChangeAnyProc] POP EAX JMP _VarNot @@1: PUSH EAX CALL _VarToInt NOT EAX MOV EDX,EAX POP EAX JMP _VarFromInt @@2: JMP VarInvalidOp @@3: MOV DX,[EAX].TVarData.VBoolean NEG DX SBB EDX,EDX NOT EDX MOV [EAX].TVarData.VBoolean,DX @@4: end; procedure _VarCopyNoInd; asm JMP VarCopyNoInd end; procedure VariantClr; asm JMP _VarClr end; procedure _VarClr; asm PUSH EAX CALL _VarClear POP EAX end; procedure VariantAddRef; asm JMP _VarAddRef end; procedure _VarAddRef; asm CMP [EAX].TVarData.VType,varOleStr JB @@1 PUSH [EAX].Integer[12] PUSH [EAX].Integer[8] PUSH [EAX].Integer[4] PUSH [EAX].Integer[0] MOV [EAX].TVarData.VType,varEmpty MOV EDX,ESP CALL _VarCopy ADD ESP,16 @@1: end; function VarType(const V: Variant): Integer; asm MOVZX EAX,[EAX].TVarData.VType end; function VarAsType(const V: Variant; VarType: Integer): Variant; begin _VarCast(Result, V, VarType); end; function VarIsEmpty(const V: Variant): Boolean; begin with TVarData(V) do Result := (VType = varEmpty) or ((VType = varDispatch) or (VType = varUnknown)) and (VDispatch = nil); end; function VarIsNull(const V: Variant): Boolean; begin Result := TVarData(V).VType = varNull; end; function VarToStr(const V: Variant): string; begin if TVarData(V).VType <> varNull then Result := V else Result := ''; end; function VarFromDateTime(DateTime: TDateTime): Variant; begin _VarClear(Result); TVarData(Result).VType := varDate; TVarData(Result).VDate := DateTime; end; function VarToDateTime(const V: Variant): TDateTime; var Temp: TVarData; begin Temp.VType := varEmpty; _VarCast(Variant(Temp), V, varDate); Result := Temp.VDate; end; function _WriteVariant(var T: Text; const V: Variant; Width: Integer): Pointer; var S: string; begin if TVarData(V).VType >= varSmallint then S := V; Write(T, S: Width); Result := @T; end; function _Write0Variant(var T: Text; const V: Variant): Pointer; begin Result := _WriteVariant(T, V, 0); end; { ----------------------------------------------------- } { Variant array support } { ----------------------------------------------------- } function VarArrayCreate(const Bounds: array of Integer; VarType: Integer): Variant; var I, DimCount: Integer; VarArrayRef: PVarArray; VarBounds: array[0..63] of TVarArrayBound; begin if not Odd(High(Bounds)) or (High(Bounds) > 127) then Error(reVarArrayCreate); DimCount := (High(Bounds) + 1) div 2; for I := 0 to DimCount - 1 do with VarBounds[I] do begin LowBound := Bounds[I * 2]; ElementCount := Bounds[I * 2 + 1] - LowBound + 1; end; VarArrayRef := SafeArrayCreate(VarType, DimCount, VarBounds); if VarArrayRef = nil then Error(reVarArrayCreate); _VarClear(Result); TVarData(Result).VType := VarType or varArray; TVarData(Result).VArray := VarArrayRef; end; function VarArrayOf(const Values: array of Variant): Variant; var I: Integer; begin Result := VarArrayCreate([0, High(Values)], varVariant); for I := 0 to High(Values) do Result[I] := Values[I]; end; procedure _VarArrayRedim(var A : Variant; HighBound: Integer); var VarBound: TVarArrayBound; begin if (TVarData(A).VType and (varArray or varByRef)) <> varArray then Error(reVarNotArray); with TVarData(A).VArray^ do VarBound.LowBound := Bounds[DimCount - 1].LowBound; VarBound.ElementCount := HighBound - VarBound.LowBound + 1; if SafeArrayRedim(TVarData(A).VArray, VarBound) <> 0 then Error(reVarArrayCreate); end; function GetVarArray(const A: Variant): PVarArray; begin if TVarData(A).VType and varArray = 0 then Error(reVarNotArray); if TVarData(A).VType and varByRef <> 0 then Result := PVarArray(TVarData(A).VPointer^) else Result := TVarData(A).VArray; end; function VarArrayDimCount(const A: Variant): Integer; begin if TVarData(A).VType and varArray <> 0 then Result := GetVarArray(A)^.DimCount else Result := 0; end; function VarArrayLowBound(const A: Variant; Dim: Integer): Integer; begin if SafeArrayGetLBound(GetVarArray(A), Dim, Result) <> 0 then Error(reVarArrayBounds); end; function VarArrayHighBound(const A: Variant; Dim: Integer): Integer; begin if SafeArrayGetUBound(GetVarArray(A), Dim, Result) <> 0 then Error(reVarArrayBounds); end; function VarArrayLock(const A: Variant): Pointer; begin if SafeArrayAccessData(GetVarArray(A), Result) <> 0 then Error(reVarNotArray); end; procedure VarArrayUnlock(const A: Variant); begin if SafeArrayUnaccessData(GetVarArray(A)) <> 0 then Error(reVarNotArray); end; function VarArrayRef(const A: Variant): Variant; begin if TVarData(A).VType and varArray = 0 then Error(reVarNotArray); _VarClear(Result); TVarData(Result).VType := TVarData(A).VType or varByRef; if TVarData(A).VType and varByRef <> 0 then TVarData(Result).VPointer := TVarData(A).VPointer else TVarData(Result).VPointer := @TVarData(A).VArray; end; function VarIsArray(const A: Variant): Boolean; begin Result := TVarData(A).VType and varArray <> 0; end; function _VarArrayGet(var A: Variant; IndexCount: Integer; Indices: Integer): Variant; cdecl; var VarArrayPtr: PVarArray; VarType: Integer; P: Pointer; begin if TVarData(A).VType and varArray = 0 then Error(reVarNotArray); VarArrayPtr := GetVarArray(A); if VarArrayPtr^.DimCount <> IndexCount then Error(reVarArrayBounds); VarType := TVarData(A).VType and varTypeMask; _VarClear(Result); if VarType = varVariant then begin if SafeArrayPtrOfIndex(VarArrayPtr, @Indices, P) <> 0 then Error(reVarArrayBounds); Result := PVariant(P)^; end else begin if SafeArrayGetElement(VarArrayPtr, @Indices, @TVarData(Result).VPointer) <> 0 then Error(reVarArrayBounds); TVarData(Result).VType := VarType; end; end; procedure _VarArrayPut(var A: Variant; const Value: Variant; IndexCount: Integer; Indices: Integer); cdecl; type TAnyPutArrayProc = procedure (var A: Variant; const Value: Variant; Index: Integer); var VarArrayPtr: PVarArray; VarType: Integer; P: Pointer; Temp: TVarData; begin if TVarData(A).VType and varArray = 0 then Error(reVarNotArray); VarArrayPtr := GetVarArray(A); if VarArrayPtr^.DimCount <> IndexCount then Error(reVarArrayBounds); VarType := TVarData(A).VType and varTypeMask; if (VarType = varVariant) and (TVarData(Value).VType <> varString) then begin if SafeArrayPtrOfIndex(VarArrayPtr, @Indices, P) <> 0 then Error(reVarArrayBounds); PVariant(P)^ := Value; end else begin Temp.VType := varEmpty; try if VarType = varVariant then begin VarStringToOleStr(Variant(Temp), Value); P := @Temp; end else begin _VarCast(Variant(Temp), Value, VarType); case VarType of varOleStr, varDispatch, varUnknown: P := Temp.VPointer; else P := @Temp.VPointer; end; end; if SafeArrayPutElement(VarArrayPtr, @Indices, P) <> 0 then Error(reVarArrayBounds); finally _VarClear(Variant(Temp)); end; end; end; function VarArrayGet(const A: Variant; const Indices: array of Integer): Variant; asm { ->EAX Pointer to A } { EDX Pointer to Indices } { ECX High bound of Indices } { [EBP+8] Pointer to result } PUSH EBX MOV EBX,ECX INC EBX JLE @@endLoop @@loop: PUSH [EDX+ECX*4].Integer DEC ECX JNS @@loop @@endLoop: PUSH EBX PUSH EAX MOV EAX,[EBP+8] PUSH EAX CALL _VarArrayGet LEA ESP,[ESP+EBX*4+3*4] POP EBX end; procedure VarArrayPut(var A: Variant; const Value: Variant; const Indices: array of Integer); asm { ->EAX Pointer to A } { EDX Pointer to Value } { ECX Pointer to Indices } { [EBP+8] High bound of Indices } PUSH EBX MOV EBX,[EBP+8] TEST EBX,EBX JS @@endLoop @@loop: PUSH [ECX+EBX*4].Integer DEC EBX JNS @@loop @@endLoop: MOV EBX,[EBP+8] INC EBX PUSH EBX PUSH EDX PUSH EAX CALL _VarArrayPut LEA ESP,[ESP+EBX*4+3*4] POP EBX end; { 64-bit Integer helper routines - recycling C++ RTL routines } procedure __llmul; external; {$L _LL } procedure __lldiv; external; { _LL } procedure __llmod; external; { _LL } procedure __llmulo; external; { _LL (overflow version) } procedure __lldivo; external; { _LL (overflow version) } procedure __llmodo; external; { _LL (overflow version) } procedure __llshl; external; { _LL } procedure __llushr; external; { _LL } procedure __llumod; external; { _LL } procedure __lludiv; external; { _LL } 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; external; {$L ReadInt64 } 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 > $0FFFFFFFFFFFFFFF) 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 > $7FFFFFFFFFFFFFFF 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; type PLongint = ^Longint; PointerArray = array [0..512*1024*1024 -2] of Pointer; PPointerArray = ^PointerArray; PDynArrayTypeInfo = ^TDynArrayTypeInfo; TDynArrayTypeInfo = packed record kind: Byte; name: string[0]; elSize: Longint; elType: ^PDynArrayTypeInfo; varType: Integer; 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: PDynArrayTypeInfo; 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(typeInfo.name)); elSize := typeInfo.elSize; if typeInfo.elType <> nil then typeInfo := 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); 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; type TBoundArray = array of Integer; PPointer = ^Pointer; { 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 a DynamicArray in a format usable for creating a Variant. // i.e. The format of the bounds returns contains pairs of lo and hi bounds where // lo is always 0, and hi is the size dimension of the array-1. function DynArrayVariantBounds(const DynArray: Pointer; typeInfo: PDynArrayTypeInfo): TBoundArray; var Dim, I: Integer; P: Pointer; begin P := DynArray; Dim := DynArrayDim(typeInfo); SetLength(Result, Dim*2); I := 0; while I < dim*2 do begin Result[I] := 0; // Always use 0 as low-bound in low/high pair Inc(I); if P <> nil then begin Result[I] := DynArraySize(P)-1; // Adjust for 0-base low-bound P := PPointerArray(p)[0]; // Assume rectangular arrays end; Inc(I); 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; // The dynamicArrayTypeInformation contains the VariantType of the element type // when the kind == tkDynArray. This function returns that VariantType. function DynArrayVarType(typeInfo: PDynArrayTypeInfo): Integer; begin Result := varNull; if (typeInfo <> nil) and (typeInfo.Kind = tkDynArray) then begin Inc(PChar(typeInfo), Length(typeInfo.name)); Result := typeInfo.varType; end; { NOTE: DECL.H and SYSTEM.PAS have different values for varString } if Result = $48 then Result := varString; end; type IntegerArray = array[0..$effffff] of Integer; PIntegerArray = ^IntegerArray; PSmallInt = ^SmallInt; PInteger = ^Integer; PSingle = ^Single; PDouble = ^Double; PDate = ^Double; PDispatch = ^IDispatch; PPDispatch = ^PDispatch; PError = ^LongWord; PWordBool = ^WordBool; PUnknown = ^IUnknown; PPUnknown = ^PUnknown; PByte = ^Byte; PPWideChar = ^PWideChar; { 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; // Copy Contents of Dynamic Array to Variant // NOTE: The Dynamic array must be rectangular // The Dynamic array must contain items whose type is Automation compatible // In case of failure, the function returns with a Variant of type VT_EMPTY. procedure DynArrayToVariant(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer); var VarBounds, Bounds, Indices: TBoundArray; DAVarType, VVarType, DynDim: Integer; PDAData: Pointer; Value: Variant; begin VarBounds := nil; Bounds := nil; { This resets the Variant to VT_EMPTY - flag which is used to determine whether the } { the cast to Variant succeeded or not } VarClear(V); { Get variantType code from DynArrayTypeInfo } DAVarType := DynArrayVarType(PDynArrayTypeInfo(TypeInfo)); { Validate the Variant Type } if ((DAVarType > varNull) and (DAVarType <= varByte)) or (DAVarType = varString) then begin {NOTE: Map varString to varOleStr for SafeArrayCreate call } if DAVarType = varString then VVarType := varOleStr else VVarType := DAVarType; { Get dimension of Dynamic Array } DynDim := DynarrayDim(PDynArrayTypeInfo(TypeInfo)); { If more than one dimension, make sure we're dealing with a rectangular array } if DynDim > 1 then if not IsDynArrayRectangular(DynArray, PDynArrayTypeInfo(TypeInfo)) then Exit; { Get Variant-style Bounds (lo/hi pair) of Dynamic Array } VarBounds := DynArrayVariantBounds(DynArray, TypeInfo); { Get DynArray Bounds } Bounds := DynArrayBounds(DynArray, TypeInfo); Indices:= Copy(Bounds); { Create Variant of SAFEARRAY } V := VarArrayCreate(VarBounds, VVarType); Assert(VarArrayDimCount(V) = DynDim); repeat PDAData := DynArrayIndex(DynArray, Indices, TypeInfo); if PDAData <> nil then begin case DAVarType of varSmallInt: Value := PSmallInt(PDAData)^; varInteger: Value := PInteger(PDAData)^; varSingle: value := PSingle(PDAData)^; varDouble: value := PDouble(PDAData)^; varCurrency: Value := PCurrency(PDAData)^; varDate: Value := PDouble(PDAData)^; varOleStr: Value := PWideString(PDAData)^; varDispatch: Value := PDispatch(PDAData)^; varError: Value := PError(PDAData)^; varBoolean: Value := PWordBool(PDAData)^; varVariant: Value := PVariant(PDAData)^; varUnknown: Value := PUnknown(PDAData)^; varByte: Value := PByte(PDAData)^; varString: Value := PString(PDAData)^; else VarClear(Value); end; { case } VarArrayPut(V, Value, Indices); end; until not DecIndices(Indices, Bounds); end; end; // Copies data from the Variant to the DynamicArray procedure DynArrayFromVariant(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer); var DADimCount, VDimCount : Integer; DAVarType, I: Integer; lengthVec: PLongInt; Bounds, Indices: TBoundArray; Value: Variant; PDAData: Pointer; begin { Get Variant information } VDimCount:= VarArrayDimCount(V); { Allocate vector for lengths } GetMem(lengthVec, VDimCount * sizeof(Integer)); { Initialize lengths - NOTE: VarArrayxxxxBound are 1-based.} for I := 0 to VDimCount-1 do PIntegerArray(lengthVec)[I]:= (VarArrayHighBound(V, I+1) - VarArrayLowBound(V, I+1)) + 1; { Set Length of DynArray } DynArraySetLength(DynArray, PDynArrayTypeInfo(TypeInfo), VDimCount, lengthVec); { Get DynArray information } DADimCount:= DynArrayDim(PDynArrayTypeInfo(TypeInfo)); DAVarType := DynArrayVarType(PDynArrayTypeInfo(TypeInfo)); Assert(VDimCount = DADimCount); { Get DynArray Bounds } Bounds := DynArrayBounds(DynArray, TypeInfo); Indices:= Copy(Bounds); { Copy data over} repeat Value := VarArrayGet(V, Indices); PDAData := DynArrayIndex(DynArray, Indices, TypeInfo); case DAVarType of varSmallInt: PSmallInt(PDAData)^ := Value; varInteger: PInteger(PDAData)^ := Value; varSingle: PSingle(PDAData)^ := Value; varDouble: PDouble(PDAData)^ := Value; varCurrency: PCurrency(PDAData)^ := Value; varDate: PDouble(PDAData)^ := Value; varOleStr: PWideString(PDAData)^ := Value; varDispatch: PDispatch(PDAData)^ := Value; varError: PError(PDAData)^ := Value; varBoolean: PWordBool(PDAData)^ := Value; varVariant: PVariant(PDAData)^ := Value; varUnknown: PUnknown(PDAData)^ := value; varByte: PByte(PDAData)^ := Value; varString: PString(PDAData)^ := Value; end; { case } until not DecIndices(Indices, Bounds); { Free vector of lengths } FreeMem(lengthVec); end; { Package/Module registration/unregistration } const LOCALE_SABBREVLANGNAME = $00000003; { abbreviated language name } LOAD_LIBRARY_AS_DATAFILE = 2; HKEY_CURRENT_USER = $80000001; KEY_ALL_ACCESS = $000F003F; OldLocaleOverrideKey = 'Software\Borland\Delphi\Locales'; // do not localize NewLocaleOverrideKey = 'Software\Borland\Locales'; // do not localize function FindHInstance(Address: Pointer): LongWord; var MemInfo: TMemInfo; begin VirtualQuery(Address, MemInfo, SizeOf(MemInfo)); if MemInfo.State = $1000{MEM_COMMIT} then Result := Longint(MemInfo.AllocationBase) else Result := 0; end; function FindClassHInstance(ClassType: TClass): LongWord; begin Result := FindHInstance(Pointer(ClassType)); 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 := CurModule.ResInstance; Exit; end; CurModule := CurModule.Next; end; Result := Instance; end; function LoadResourceModule(ModuleName: PChar): LongWord; var FileName: array[0..260] 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): PChar; var CurrBS, NextBS: PChar; Handle, L: Integer; FindData: TWin32FindData; Buffer: array[0..260] 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 lstrcpy(AFileName, Buffer); 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 + lstrlen(FindData.cFileName) + 1 > SizeOf(Buffer) then Exit; Buffer[L] := '\'; lstrcpy(Buffer + L + 1, FindData.cFileName); Inc(L, lstrlen(FindData.cFileName) + 1); CurrBS := NextBS; end; lstrcpy(AFileName, Buffer); end; begin GetModuleFileName(0, FileName, SizeOf(FileName)); // Get host appliation name LocaleOverride[0] := #0; if (RegOpenKeyEx(HKEY_CURRENT_USER, NewLocaleOverrideKey, 0, KEY_ALL_ACCESS, Key) = 0) or (RegOpenKeyEx(HKEY_CURRENT_USER, OldLocaleOverrideKey, 0, KEY_ALL_ACCESS, Key) = 0) then try Size := SizeOf(LocaleOverride); if RegQueryValueEx(Key, ToLongPath(FileName), nil, nil, LocaleOverride, @Size) <> 0 then RegQueryValueEx(Key, '', nil, nil, LocaleOverride, @Size); finally RegCloseKey(Key); end; lstrcpy(FileName, ModuleName); 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) + lstrlen(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 lstrcpy(P, LocaleOverride); 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 lstrcpy(P, LocaleName); Result := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE); if Result = 0 then begin // Finally look for a language only translation LocaleName[2] := #0; lstrcpy(P, LocaleName); Result := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE); end; end; end; end; end; 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(CurModule.ResInstance, 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; 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-} { ResString support function } function LoadResString(ResStringRec: PResStringRec): string; var Buffer: array[0..1023] of Char; begin if ResStringRec <> nil then if ResStringRec.Identifier < 64*1024 then SetString(Result, Buffer, LoadString(FindResourceHInstance(ResStringRec.Module^), ResStringRec.Identifier, Buffer, SizeOf(Buffer))) else Result := PChar(ResStringRec.Identifier); end; procedure _IntfClear(var Dest: IUnknown); asm MOV EDX,[EAX] TEST EDX,EDX JE @@1 MOV DWORD PTR [EAX],0 PUSH EAX PUSH EDX MOV EAX,[EDX] CALL [EAX].vmtRelease.Pointer POP EAX @@1: end; procedure _IntfCopy(var Dest: IUnknown; const Source: IUnknown); asm MOV ECX,[EAX] { save dest } MOV [EAX],EDX { assign dest } TEST EDX,EDX { need to addref source before releasing dest } JE @@1 { to make self assignment (I := I) work right } PUSH ECX PUSH EDX MOV EAX,[EDX] CALL [EAX].vmtAddRef.Pointer POP ECX @@1: TEST ECX,ECX JE @@2 PUSH ECX MOV EAX,[ECX] CALL [EAX].vmtRelease.Pointer @@2: end; procedure _IntfCast(var Dest: IUnknown; const Source: IUnknown; const IID: TGUID); 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 [EAX].vmtRelease.Pointer MOV EDX,[ESP] @@1: MOV EAX,[EDX] CALL [EAX].vmtQueryInterface.Pointer TEST EAX,EAX JE @@2 MOV AL,reIntfCastError JMP Error @@2: end; procedure _IntfAddRef(const Dest: IUnknown); 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; const E_NOINTERFACE = HResult($80004002); 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; procedure _CheckAutoResult; 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; procedure _IntfDispCall; asm JMP DispCallByIDProc end; procedure _IntfVarCall; asm end; function CompToDouble(acomp: Comp): Double; cdecl; begin Result := acomp; end; procedure DoubleToComp(adouble: Double; var result: Comp); cdecl; begin result := adouble; end; function CompToCurrency(acomp: Comp): Currency; cdecl; begin Result := acomp; end; procedure CurrencyToComp(acurrency: Currency; var result: Comp); cdecl; begin result := acurrency end; function GetMemory(Size: Integer): Pointer; cdecl; begin Result := {X- SysGetMem(Size); -replaced to use current memory manager} MemoryManager.GetMem( Size ); end; function FreeMemory(P: Pointer): Integer; cdecl; begin if P = nil then Result := 0 else Result := {X- SysFreeMem(P); - replaced to use current memory manager} MemoryManager.FreeMem( P ); end; function ReallocMemory(P: Pointer; Size: Integer): Pointer; cdecl; begin {X- Result := SysReallocMem(P, Size); - replaced to use current memory manager} Result := MemoryManager.ReallocMem( P, Size ); end; function GetCurrentThreadId: DWORD; stdcall; external kernel name 'GetCurrentThreadId'; {X} // convert var CmdLine : PChar to a function: {X} function CmdLine : PChar; {X} begin {X} Result := GetCommandLine; {X} end; initialization {X- initialized by 0 anyway ExitCode := 0; ErrorAddr := nil; RandSeed := 0; X+} {X- initialized statically FileMode := 2; Test8086 := 2; Test8087 := 3; X+} {X- moved to SysVarnt.pas TVarData(Unassigned).VType := varEmpty; TVarData(Null).VType := varNull; TVarData(EmptyParam).VType := varError; TVarData(EmptyParam).VError := $80020004; //DISP_E_PARAMNOTFOUND ClearAnyProc := @VarInvalidOp; ChangeAnyProc := @VarCastError; RefAnyProc := @VarInvalidOp; X+} {X- if _isNECWindows then _FpuMaskInit; FpuInit(); X+} {X- to use Input/Output, call UseInputOutput (or include following two lines into your code and call Close(Input), Close(Output) at the end of execution). _Assign( Input, '' ); _Assign( Output, '' ); X+} {X- CmdLine := GetCommandLine; converted to a function } {X- CmdShow := GetCmdShow; converted to a function } MainThreadID := GetCurrentThreadID; finalization {X}if assigned( CloseInputOutput ) then {X} CloseInputOutput; {X- Close(Input); Close(Output); X+} {X UninitAllocator; - replaced with call to UninitMemoryManager handler. } UninitMemoryManager; end.