GR32_LowLevel.pas 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112
  1. unit GR32_LowLevel;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Alex A. Denisov
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2000-2009
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. interface
  32. {$include GR32.inc}
  33. {$IFDEF PUREPASCAL}
  34. {$DEFINE USENATIVECODE}
  35. {$DEFINE USEMOVE}
  36. {$ENDIF}
  37. {$IFDEF USEINLINING}
  38. {$DEFINE USENATIVECODE}
  39. {$ENDIF}
  40. // Define WRAP_USEFLOATMOD to have Wrap(Single, Single) forward to FloatMod().
  41. // If WRAP_USEFLOATMOD is not defined then an iterative algorithm is used which is
  42. // very ineffective when the value is much out of bounds.
  43. {$define WRAP_USEFLOATMOD}
  44. uses
  45. {$if defined(FRAMEWORK_FMX)}
  46. FMX.Graphics,
  47. {$elseif defined(FRAMEWORK_VCL)}
  48. VCL.Graphics,
  49. {$else}
  50. Graphics,
  51. {$ifend}
  52. System.UITypes,
  53. GR32,
  54. GR32_Math,
  55. GR32_Bindings;
  56. //------------------------------------------------------------------------------
  57. //
  58. // FillLongword: An analogue of FillChar for 32 bit values
  59. //
  60. //------------------------------------------------------------------------------
  61. var FillLongword: procedure(var X; Count: Cardinal; Value: Longword);
  62. procedure FillWord(var X; Count: Cardinal; Value: Longword);
  63. //------------------------------------------------------------------------------
  64. //
  65. // MoveLongword: An analogue of Move optimized for 32 bit values
  66. // MoveWord: An analogue of Move optimized for 16 bit values
  67. //
  68. //------------------------------------------------------------------------------
  69. {$IFDEF USEMOVE}
  70. procedure MoveLongword(const Source; var Dest; Count: Integer); {$IFDEF USEINLINING} inline; {$ENDIF}
  71. {$ELSE}
  72. procedure MoveLongword(const Source; var Dest; Count: Integer);
  73. {$ENDIF}
  74. procedure MoveWord(const Source; var Dest; Count: Integer);
  75. //------------------------------------------------------------------------------
  76. //
  77. // StackAlloc: Allocates a 'small' block of memory on the stack
  78. //
  79. //------------------------------------------------------------------------------
  80. {$IFDEF USESTACKALLOC}
  81. function StackAlloc(Size: Integer): Pointer; register;
  82. // Pops memory allocated by StackAlloc
  83. procedure StackFree(P: Pointer); register;
  84. {$ENDIF}
  85. //------------------------------------------------------------------------------
  86. //
  87. // Swap: Exchange values
  88. //
  89. //------------------------------------------------------------------------------
  90. // Exchange two 32-bit values (except Swap(pointer, pointer))
  91. procedure Swap(var A, B: Pointer); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  92. procedure Swap(var A, B: Integer); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  93. procedure Swap(var A, B: TFixed); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  94. procedure Swap(var A, B: TColor32); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  95. procedure Swap32(var A, B); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  96. // Convert little-endian <-> big-endian
  97. function Swap16(Value: Word): Word; {$IFDEF USENATIVECODE} inline; {$ENDIF}
  98. function Swap32(Value: Cardinal): Cardinal; overload; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  99. function Swap64(Value: Int64): Int64; {$IFDEF USEINLINING} inline; {$ENDIF}
  100. // Exchange A <-> B only if B < A
  101. procedure TestSwap(var A, B: Integer); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  102. procedure TestSwap(var A, B: TFixed); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  103. // Exchange A <-> B only if B < A then restrict both to [0..Size-1] range.
  104. // Returns true if resulting range has common points with [0..Size-1] range.
  105. function TestClip(var A, B: Integer; const Size: Integer): Boolean; overload;
  106. function TestClip(var A, B: Integer; const Start, Stop: Integer): Boolean; overload;
  107. //------------------------------------------------------------------------------
  108. //
  109. // Min/Max: Returns min./max. value of A, B and C
  110. //
  111. //------------------------------------------------------------------------------
  112. function Min(const A, B, C: Integer): Integer; overload; {$IFDEF USENATIVECODE} inline; {$ENDIF}
  113. function Max(const A, B, C: Integer): Integer; overload; {$IFDEF USENATIVECODE} inline; {$ENDIF}
  114. //------------------------------------------------------------------------------
  115. //
  116. // Constrain, Clamp: Constrain value to range
  117. //
  118. //------------------------------------------------------------------------------
  119. // Return value constrained to [Lo..Hi] range
  120. function Constrain(const Value, Lo, Hi: Integer): Integer; overload; {$IFDEF USENATIVECODE} inline; {$ENDIF}
  121. function Constrain(const Value, Lo, Hi: Single): Single; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  122. // Returns value constrained to [min(Constrain1, Constrain2)..max(Constrain1, Constrain2] range
  123. function SwapConstrain(const Value: Integer; Constrain1, Constrain2: Integer): Integer;
  124. // Clamp integer value to [0..255] range
  125. function Clamp(const Value: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  126. // Clamp integer value to [0..Max] range
  127. function Clamp(Value, Max: Integer): Integer; overload; {$IFDEF USENATIVECODE} inline; {$ENDIF}
  128. // Clamp integer value to [Min..Max] range. Same as Constrain with same parameters.
  129. function Clamp(Value, Min, Max: Integer): Integer; overload; {$IFDEF USENATIVECODE} inline; {$ENDIF}
  130. //------------------------------------------------------------------------------
  131. //
  132. // Wrap: Constrain value to range with wrap around
  133. //
  134. //------------------------------------------------------------------------------
  135. // Wrap integer value to [0..Max] range
  136. function Wrap(Value, Max: Integer): Integer; overload; {$IFDEF USENATIVECODE} inline; {$ENDIF}
  137. // Same but [Min..Max] range. Min is assumed to be <= Max
  138. function Wrap(Value, Min, Max: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  139. // Wrap single value to [0..Max) range.
  140. // Basically the same as FloatMod except:
  141. // - The upper limit is expected to always be positive.
  142. // - If Max=0, then 0 is returned.
  143. // Unlike the integer version of Wrap, the upper limit is exclusive.
  144. // NAN is not checked.
  145. function Wrap(Value, Max: Single): Single; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  146. // Same as Wrap above but Value is by ref and Max is an integer
  147. procedure WrapMem(var Value: Single; Max: Cardinal); {$IFDEF USEINLINING} inline; {$ENDIF}
  148. // Fast Wrap alternatives for cases where range+1 is a power of two (e.g. 3, 7, 15, etc)
  149. function WrapPow2(Value, Max: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  150. function WrapPow2(Value, Min, Max: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  151. //------------------------------------------------------------------------------
  152. //
  153. // Mirror, Reflect: Constrain value to range with mirroring
  154. //
  155. //------------------------------------------------------------------------------
  156. // Mirror:
  157. // - Has symmetry around the center of the edge value/pixel.
  158. // - Cycle is (2*Max).
  159. // - Example (Max=3): 012321012321 (2 cycles)
  160. // - Example (Max=4): 0123432101234321 (2 cycles)
  161. //
  162. // Reflect:
  163. // - Has symmetry around the end of the edge value/pixel.
  164. // - Cycle is (2*(Max+1)).
  165. // - Example (Max=3): 0123321001233210 (2 cycles)
  166. // - Example (Max=4): 01234432100123443210 (2 cycles)
  167. //------------------------------------------------------------------------------
  168. // Mirror integer value in [0..Max] range
  169. function Mirror(Value, Max: Integer): Integer; overload;
  170. // Mirror integer value in [Min..Max] range
  171. function Mirror(Value, Min, Max: Integer): Integer; overload;
  172. // Reflect integer value in [0..Max] range
  173. function Reflect(Value, Max: Integer): Integer; overload;
  174. // Reflect integer value in [Min..Max] range
  175. function Reflect(Value, Min, Max: Integer): Integer; overload;
  176. // Fast Reflect alternatives for cases where range+1 is a power of two (e.g. 3, 7, 15, etc)
  177. function ReflectPow2(Value, Max: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  178. function ReflectPow2(Value, Min, Max: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  179. //------------------------------------------------------------------------------
  180. //
  181. // Clamp/Wrap/Mirror/Reflect
  182. //
  183. //------------------------------------------------------------------------------
  184. // Functions to determine appropiate wrap procs (normal or power of 2 optimized)
  185. function GetOptimalWrap(Max: Integer): TWrapProc; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  186. function GetOptimalWrap(Min, Max: Integer): TWrapProcEx; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  187. function GetOptimalReflect(Max: Integer): TWrapProc; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  188. function GetOptimalReflect(Min, Max: Integer): TWrapProcEx; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  189. // Functions to retrieve correct WrapProc given WrapMode (and range) }
  190. function GetWrapProc(WrapMode: TWrapMode): TWrapProc; overload;
  191. function GetWrapProc(WrapMode: TWrapMode; Max: Integer): TWrapProc; overload;
  192. function GetWrapProcEx(WrapMode: TWrapMode): TWrapProcEx; overload;
  193. function GetWrapProcEx(WrapMode: TWrapMode; Min, Max: Integer): TWrapProcEx; overload;
  194. const
  195. WRAP_PROCS: array[TWrapMode] of TWrapProc = (Clamp, Wrap, Mirror{$ifdef GR32_WRAPMODE_REFLECT}, Reflect{$endif});
  196. WRAP_PROCS_EX: array[TWrapMode] of TWrapProcEx = (Clamp, Wrap, Mirror{$ifdef GR32_WRAPMODE_REFLECT}, Reflect{$endif});
  197. //------------------------------------------------------------------------------
  198. //
  199. // Div255: Fast integer division by 255 with limited range
  200. //
  201. //------------------------------------------------------------------------------
  202. // Fast integer division by 255.
  203. // Valid for the range [0..$ffff]
  204. function Div255(Value: Word): Word; {$IFDEF USEINLINING} inline; {$ENDIF}
  205. // Possibly even faster integer division by 255.
  206. // Valid for the range [0..255*255] }
  207. function FastDiv255(Value: Word): Word; {$IFDEF USEINLINING} inline; {$ENDIF}
  208. // Fast rounded integer division by 255.
  209. // Valid for the range [0..255*255]
  210. function Div255Round(Value: Word): Word; {$IFDEF USEINLINING} inline; {$ENDIF}
  211. //------------------------------------------------------------------------------
  212. //
  213. // FastRound, FastTrunc, FastFloor, and FastCeil
  214. // Fast alternatives to the RTL Round, Trunc, Floor and Ceil
  215. //
  216. //------------------------------------------------------------------------------
  217. function FastFloor(Value: TFloat): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  218. function FastFloor(Value: Double): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  219. function FastCeil(Value: TFloat): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  220. function FastCeil(Value: Double): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  221. type
  222. TFastRoundSingleProc = function(Value: TFloat): Integer;
  223. TFastRoundDoubleProc = function(Value: Double): Integer;
  224. var
  225. // Trunc, Round, Floor, Ceil bindings
  226. FastTrunc: TFastRoundSingleProc;
  227. FastRound: TFastRoundSingleProc;
  228. FastFloorSingle: TFastRoundSingleProc;
  229. FastFloorDouble: TFastRoundDoubleProc;
  230. FastCeilSingle: TFastRoundSingleProc;
  231. FastCeilDouble: TFastRoundDoubleProc;
  232. //------------------------------------------------------------------------------
  233. //
  234. // SAR: Shift right with sign conservation
  235. //
  236. //------------------------------------------------------------------------------
  237. // Note that for PUREPASCAL SAR_n(x) is implemented as (x div 2^n).
  238. // This works for positive values but not for negative values as both Delphi and FPC
  239. // compiles (x div 2^n) to:
  240. //
  241. // ADD EAX, $00007FFF
  242. // TEST EAX, EAX
  243. // JNS :positive
  244. // ADD EAX, $0000FFFF
  245. // :positive
  246. // SAR EAX, n
  247. //
  248. function SAR_3(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  249. function SAR_4(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  250. function SAR_6(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  251. function SAR_8(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  252. function SAR_9(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  253. function SAR_11(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  254. function SAR_12(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  255. function SAR_13(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  256. function SAR_14(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  257. function SAR_15(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  258. function SAR_16(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
  259. //------------------------------------------------------------------------------
  260. //
  261. // ColorSwap exchanges ARGB <-> ABGR and fills A with $FF
  262. //
  263. //------------------------------------------------------------------------------
  264. function ColorSwap(WinColor: TColor): TColor32;
  265. //------------------------------------------------------------------------------
  266. //
  267. // Bindings
  268. //
  269. //------------------------------------------------------------------------------
  270. var
  271. LowLevelRegistry: TFunctionRegistry;
  272. const
  273. FID_FILLLONGWORD = 0;
  274. FID_FAST_TRUNC = 1;
  275. FID_FAST_ROUND = 2;
  276. //------------------------------------------------------------------------------
  277. //------------------------------------------------------------------------------
  278. //------------------------------------------------------------------------------
  279. implementation
  280. uses
  281. {$if not defined(FPC)}
  282. System.Math,
  283. {$else}
  284. SysUtils,
  285. Math,
  286. {$ifend}
  287. GR32.Types.SIMD;
  288. {$R-}{$Q-} // switch off overflow and range checking
  289. //------------------------------------------------------------------------------
  290. //
  291. // FillLongword
  292. //
  293. //------------------------------------------------------------------------------
  294. procedure FillLongword_Pas(var X; Count: Cardinal; Value: Longword);
  295. var
  296. I: Integer;
  297. P: PIntegerArray;
  298. begin
  299. P := PIntegerArray(@X);
  300. for I := Count - 1 downto 0 do
  301. P[I] := Integer(Value);
  302. end;
  303. {$IFNDEF PUREPASCAL}
  304. procedure FillLongword_ASM(var X; Count: Cardinal; Value: Longword); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  305. asm
  306. {$IFDEF TARGET_x86}
  307. // EAX = X; EDX = Count; ECX = Value
  308. PUSH EDI
  309. MOV EDI,EAX // Point EDI to destination
  310. MOV EAX,ECX
  311. MOV ECX,EDX
  312. REP STOSD // Fill count dwords
  313. @Exit:
  314. POP EDI
  315. {$ENDIF}
  316. {$IFDEF TARGET_x64}
  317. // ECX = X; EDX = Count; R8 = Value
  318. PUSH RDI
  319. MOV RDI,RCX // Point EDI to destination
  320. MOV RAX,R8 // copy value from R8 to RAX (EAX)
  321. MOV ECX,EDX // copy count to ECX
  322. TEST ECX,ECX
  323. JS @Exit
  324. REP STOSD // Fill count dwords
  325. @Exit:
  326. POP RDI
  327. {$ENDIF}
  328. end;
  329. {$IFNDEF OMIT_SSE2}
  330. procedure FillLongword_SSE2(var X; Count: Integer; Value: Longword); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  331. asm
  332. {$IFDEF TARGET_x86}
  333. // EAX = X; EDX = Count; ECX = Value
  334. TEST EDX, EDX // if Count = 0 then
  335. JZ @Exit // Exit
  336. PUSH EDI // push EDI on stack
  337. MOV EDI, EAX // Point EDI to destination
  338. CMP EDX, 32
  339. JL @SmallLoop
  340. AND EAX, 3 // get aligned count
  341. TEST EAX, EAX // check if X is not dividable by 4
  342. JNZ @SmallLoop // otherwise perform slow small loop
  343. MOV EAX, EDI
  344. SHR EAX, 2 // bytes to count
  345. AND EAX, 3 // get aligned count
  346. ADD EAX,-4
  347. NEG EAX // get count to advance
  348. JZ @SetupMain
  349. SUB EDX, EAX // subtract aligning start from total count
  350. @AligningLoop:
  351. MOV [EDI], ECX
  352. ADD EDI, 4
  353. DEC EAX
  354. JNZ @AligningLoop
  355. @SetupMain:
  356. MOV EAX, EDX // EAX = remaining count
  357. SHR EAX, 2
  358. SHL EAX, 2
  359. SUB EDX, EAX // EDX = remaining count
  360. SHR EAX, 2
  361. MOVD XMM0, ECX
  362. PUNPCKLDQ XMM0, XMM0
  363. PUNPCKLDQ XMM0, XMM0
  364. @SSE2Loop:
  365. MOVDQA [EDI], XMM0
  366. ADD EDI, 16
  367. DEC EAX
  368. JNZ @SSE2Loop
  369. @SmallLoop:
  370. MOV EAX,ECX
  371. MOV ECX,EDX
  372. REP STOSD // Fill count dwords
  373. @ExitPOP:
  374. POP EDI
  375. @Exit:
  376. {$ENDIF}
  377. {$IFDEF TARGET_x64}
  378. // RCX = X; RDX = Count; R8 = Value
  379. TEST RDX, RDX // if Count = 0 then
  380. JZ @Exit // Exit
  381. MOV R9, RCX // Point R9 to destination
  382. CMP RDX, 32
  383. JL @SmallLoop
  384. AND RCX, 3 // get aligned count
  385. TEST RCX, RCX // check if X is not dividable by 4
  386. JNZ @SmallLoop // otherwise perform slow small loop
  387. MOV RCX, R9
  388. SHR RCX, 2 // bytes to count
  389. AND RCX, 3 // get aligned count
  390. ADD RCX,-4
  391. NEG RCX // get count to advance
  392. JZ @SetupMain
  393. SUB RDX, RCX // subtract aligning start from total count
  394. @AligningLoop:
  395. MOV [R9], R8D
  396. ADD R9, 4
  397. DEC RCX
  398. JNZ @AligningLoop
  399. @SetupMain:
  400. MOV RCX, RDX // RCX = remaining count
  401. SHR RCX, 2
  402. SHL RCX, 2
  403. SUB RDX, RCX // RDX = remaining count
  404. SHR RCX, 2
  405. MOVD XMM0, R8D
  406. PUNPCKLDQ XMM0, XMM0
  407. PUNPCKLDQ XMM0, XMM0
  408. @SSE2Loop:
  409. MOVDQA [R9], XMM0
  410. ADD R9, 16
  411. DEC RCX
  412. JNZ @SSE2Loop
  413. TEST RDX, RDX
  414. JZ @Exit
  415. @SmallLoop:
  416. MOV [R9], R8D
  417. ADD R9, 4
  418. DEC RDX
  419. JNZ @SmallLoop
  420. @Exit:
  421. {$ENDIF}
  422. end;
  423. {$ENDIF}
  424. {$ENDIF}
  425. //------------------------------------------------------------------------------
  426. //
  427. // FillWord
  428. //
  429. //------------------------------------------------------------------------------
  430. procedure FillWord(var X; Count: Cardinal; Value: LongWord);
  431. {$IFDEF USENATIVECODE}
  432. var
  433. I: Integer;
  434. P: PWordArray;
  435. begin
  436. P := PWordArray(@X);
  437. for I := Count - 1 downto 0 do
  438. P[I] := Value;
  439. {$ELSE}
  440. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  441. asm
  442. {$IFDEF TARGET_x86}
  443. // EAX = X; EDX = Count; ECX = Value
  444. PUSH EDI
  445. MOV EDI,EAX // Point EDI to destination
  446. MOV EAX,ECX
  447. MOV ECX,EDX
  448. TEST ECX,ECX
  449. JZ @exit
  450. REP STOSW // Fill count words
  451. @exit:
  452. POP EDI
  453. {$ENDIF}
  454. {$IFDEF TARGET_x64}
  455. // ECX = X; EDX = Count; R8D = Value
  456. PUSH RDI
  457. MOV RDI,RCX // Point EDI to destination
  458. MOV EAX,R8D
  459. MOV ECX,EDX
  460. TEST ECX,ECX
  461. JZ @exit
  462. REP STOSW // Fill count words
  463. @exit:
  464. POP RDI
  465. {$ENDIF}
  466. {$ENDIF}
  467. end;
  468. //------------------------------------------------------------------------------
  469. //
  470. // MoveLongword
  471. //
  472. //------------------------------------------------------------------------------
  473. procedure MoveLongword(const Source; var Dest; Count: Integer);
  474. {$IFDEF USEMOVE}
  475. begin
  476. Move(Source, Dest, Count shl 2);
  477. {$ELSE}
  478. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  479. asm
  480. {$IFDEF TARGET_x86}
  481. // EAX = Source; EDX = Dest; ECX = Count
  482. PUSH ESI
  483. PUSH EDI
  484. MOV ESI,EAX
  485. MOV EDI,EDX
  486. CMP EDI,ESI
  487. JE @exit
  488. REP MOVSD
  489. @exit:
  490. POP EDI
  491. POP ESI
  492. {$ENDIF}
  493. {$IFDEF TARGET_x64}
  494. // RCX = Source; RDX = Dest; R8 = Count
  495. PUSH RSI
  496. PUSH RDI
  497. MOV RSI,RCX
  498. MOV RDI,RDX
  499. MOV RCX,R8
  500. CMP RDI,RSI
  501. JE @exit
  502. REP MOVSD
  503. @exit:
  504. POP RDI
  505. POP RSI
  506. {$ENDIF}
  507. {$ENDIF}
  508. end;
  509. //------------------------------------------------------------------------------
  510. //
  511. // MoveWord
  512. //
  513. //------------------------------------------------------------------------------
  514. procedure MoveWord(const Source; var Dest; Count: Integer);
  515. {$IFDEF USEMOVE}
  516. begin
  517. Move(Source, Dest, Count shl 1);
  518. {$ELSE}
  519. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  520. asm
  521. {$IFDEF TARGET_x86}
  522. // EAX = X; EDX = Count; ECX = Value
  523. PUSH ESI
  524. PUSH EDI
  525. MOV ESI,EAX
  526. MOV EDI,EDX
  527. MOV EAX,ECX
  528. CMP EDI,ESI
  529. JE @exit
  530. REP MOVSW
  531. @exit:
  532. POP EDI
  533. POP ESI
  534. {$ENDIF}
  535. {$IFDEF TARGET_x64}
  536. // RCX = Source; RDX = Dest; R8 = Count
  537. CMP RCX,RDX
  538. JE @exit
  539. TEST R8,R8
  540. JZ @exit
  541. PUSH RSI
  542. PUSH RDI
  543. MOV RSI,RCX
  544. MOV RDI,RDX
  545. MOV RCX,R8
  546. REP MOVSW
  547. POP RDI
  548. POP RSI
  549. @exit:
  550. {$ENDIF}
  551. {$ENDIF}
  552. end;
  553. //------------------------------------------------------------------------------
  554. //
  555. // Swap
  556. //
  557. //------------------------------------------------------------------------------
  558. procedure Swap(var A, B: Pointer);
  559. var
  560. T: Pointer;
  561. begin
  562. T := A;
  563. A := B;
  564. B := T;
  565. end;
  566. //------------------------------------------------------------------------------
  567. procedure Swap(var A, B: Integer);
  568. var
  569. T: Integer;
  570. begin
  571. T := A;
  572. A := B;
  573. B := T;
  574. end;
  575. //------------------------------------------------------------------------------
  576. procedure Swap(var A, B: TFixed);
  577. var
  578. T: TFixed;
  579. begin
  580. T := A;
  581. A := B;
  582. B := T;
  583. end;
  584. //------------------------------------------------------------------------------
  585. procedure Swap(var A, B: TColor32);
  586. var
  587. T: TColor32;
  588. begin
  589. T := A;
  590. A := B;
  591. B := T;
  592. end;
  593. //------------------------------------------------------------------------------
  594. procedure Swap32(var A, B);
  595. var
  596. T: Integer;
  597. begin
  598. T := Integer(A);
  599. Integer(A) := Integer(B);
  600. Integer(B) := T;
  601. end;
  602. //------------------------------------------------------------------------------
  603. function Swap16(Value: Word): Word;
  604. {$IFDEF USENATIVECODE}
  605. begin
  606. Result := System.Swap(Value);
  607. {$ELSE}
  608. asm
  609. {$IFDEF TARGET_x64}
  610. MOV EAX, ECX
  611. {$ENDIF}
  612. XCHG AL, AH
  613. {$ENDIF}
  614. end;
  615. //------------------------------------------------------------------------------
  616. function Swap32(Value: Cardinal): Cardinal;
  617. {$IFDEF PUREPASCAL}
  618. type
  619. TTwoWords = array [0..1] of Word;
  620. begin
  621. TTwoWords(Result)[1] := System.Swap(TTwoWords(Value)[0]);
  622. TTwoWords(Result)[0] := System.Swap(TTwoWords(Value)[1]);
  623. {$ELSE}
  624. asm
  625. {$IFDEF TARGET_x64}
  626. MOV EAX, ECX
  627. {$ENDIF}
  628. BSWAP EAX
  629. {$ENDIF}
  630. end;
  631. //------------------------------------------------------------------------------
  632. function Swap64(Value: Int64): Int64;
  633. type
  634. TFourWords = array [0..3] of Word;
  635. begin
  636. TFourWords(Result)[3] := System.Swap(TFourWords(Value)[0]);
  637. TFourWords(Result)[2] := System.Swap(TFourWords(Value)[1]);
  638. TFourWords(Result)[1] := System.Swap(TFourWords(Value)[2]);
  639. TFourWords(Result)[0] := System.Swap(TFourWords(Value)[3]);
  640. end;
  641. //------------------------------------------------------------------------------
  642. procedure TestSwap(var A, B: Integer);
  643. var
  644. T: Integer;
  645. begin
  646. if B < A then
  647. begin
  648. T := A;
  649. A := B;
  650. B := T;
  651. end;
  652. end;
  653. //------------------------------------------------------------------------------
  654. procedure TestSwap(var A, B: TFixed);
  655. var
  656. T: TFixed;
  657. begin
  658. if B < A then
  659. begin
  660. T := A;
  661. A := B;
  662. B := T;
  663. end;
  664. end;
  665. //------------------------------------------------------------------------------
  666. function TestClip(var A, B: Integer; const Size: Integer): Boolean;
  667. begin
  668. TestSwap(A, B); // now A = min(A,B) and B = max(A, B)
  669. if A < 0 then
  670. A := 0;
  671. if B >= Size then
  672. B := Size - 1;
  673. Result := B >= A;
  674. end;
  675. //------------------------------------------------------------------------------
  676. function TestClip(var A, B: Integer; const Start, Stop: Integer): Boolean;
  677. begin
  678. TestSwap(A, B); // now A = min(A,B) and B = max(A, B)
  679. if A < Start then
  680. A := Start;
  681. if B >= Stop then
  682. B := Stop - 1;
  683. Result := B >= A;
  684. end;
  685. //------------------------------------------------------------------------------
  686. //
  687. // Min/Max
  688. //
  689. //------------------------------------------------------------------------------
  690. function Max(const A, B, C: Integer): Integer;
  691. {$IFDEF USENATIVECODE}
  692. begin
  693. if A > B then
  694. Result := A
  695. else
  696. Result := B;
  697. if C > Result then
  698. Result := C;
  699. {$ELSE}
  700. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  701. asm
  702. {$IFDEF TARGET_x64}
  703. MOV RAX,RCX
  704. MOV RCX,R8
  705. {$ENDIF}
  706. CMP EDX,EAX
  707. CMOVG EAX,EDX
  708. CMP ECX,EAX
  709. CMOVG EAX,ECX
  710. {$ENDIF}
  711. end;
  712. //------------------------------------------------------------------------------
  713. function Min(const A, B, C: Integer): Integer;
  714. {$IFDEF USENATIVECODE}
  715. begin
  716. if A < B then
  717. Result := A
  718. else
  719. Result := B;
  720. if C < Result then
  721. Result := C;
  722. {$ELSE}
  723. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  724. asm
  725. {$IFDEF TARGET_x64}
  726. MOV RAX,RCX
  727. MOV RCX,R8
  728. {$ENDIF}
  729. CMP EDX,EAX
  730. CMOVL EAX,EDX
  731. CMP ECX,EAX
  732. CMOVL EAX,ECX
  733. {$ENDIF}
  734. end;
  735. //------------------------------------------------------------------------------
  736. //
  737. // Constrain
  738. //
  739. //------------------------------------------------------------------------------
  740. function Constrain(const Value, Lo, Hi: Integer): Integer;
  741. {$IFDEF USENATIVECODE}
  742. begin
  743. Result := Value;
  744. if Result < Lo then
  745. Result := Lo
  746. else
  747. if Result > Hi then
  748. Result := Hi;
  749. {$ELSE}
  750. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  751. asm
  752. {$IFDEF TARGET_x64}
  753. MOV EAX,ECX
  754. MOV ECX,R8D
  755. {$ENDIF}
  756. CMP EDX,EAX
  757. CMOVG EAX,EDX
  758. CMP ECX,EAX
  759. CMOVL EAX,ECX
  760. {$ENDIF}
  761. end;
  762. //------------------------------------------------------------------------------
  763. function Constrain(const Value, Lo, Hi: Single): Single; overload;
  764. begin
  765. Result := Value;
  766. if Result < Lo then
  767. Result := Lo
  768. else
  769. if Result > Hi then
  770. Result := Hi;
  771. end;
  772. //------------------------------------------------------------------------------
  773. function SwapConstrain(const Value: Integer; Constrain1, Constrain2: Integer): Integer;
  774. begin
  775. TestSwap(Constrain1, Constrain2);
  776. Result := Value;
  777. if Result < Constrain1 then
  778. Result := Constrain1
  779. else
  780. if Result > Constrain2 then
  781. Result := Constrain2;
  782. end;
  783. //------------------------------------------------------------------------------
  784. //
  785. // Clamp
  786. //
  787. //------------------------------------------------------------------------------
  788. function Clamp(const Value: Integer): Integer;
  789. {$IFDEF USENATIVECODE}
  790. begin
  791. Result := Value;
  792. if Result > 255 then
  793. Result := 255
  794. else
  795. if Result < 0 then
  796. Result := 0;
  797. {$ELSE}
  798. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  799. asm
  800. {$IFDEF TARGET_x64}
  801. // in x64 calling convention parameters are passed in ECX, EDX, R8 & R9
  802. MOV EAX,ECX
  803. {$ENDIF}
  804. TEST EAX,$FFFFFF00
  805. JNZ @1
  806. RET
  807. @1: JS @2
  808. MOV EAX,$FF
  809. RET
  810. @2: XOR EAX,EAX
  811. {$ENDIF}
  812. end;
  813. //------------------------------------------------------------------------------
  814. function Clamp(Value, Max: Integer): Integer;
  815. {$IFDEF USENATIVECODE}
  816. begin
  817. Result := Value;
  818. if Result > Max then
  819. Result := Max
  820. else
  821. if Result < 0 then
  822. Result := 0;
  823. {$ELSE}
  824. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  825. asm
  826. {$IFDEF TARGET_x64}
  827. MOV EAX,ECX
  828. MOV ECX,R8D
  829. {$ENDIF}
  830. CMP EAX,EDX
  831. JG @Above
  832. TEST EAX,EAX
  833. JL @Below
  834. RET
  835. @Above:
  836. MOV EAX,EDX
  837. RET
  838. @Below:
  839. MOV EAX,0
  840. RET
  841. {$ENDIF}
  842. end;
  843. //------------------------------------------------------------------------------
  844. function Clamp(Value, Min, Max: Integer): Integer;
  845. {$IFDEF USENATIVECODE}
  846. begin
  847. Result := Value;
  848. if Result > Max then
  849. Result := Max
  850. else
  851. if Result < Min then
  852. Result := Min;
  853. {$ELSE}
  854. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  855. asm
  856. {$IFDEF TARGET_x64}
  857. MOV EAX,ECX
  858. MOV ECX,R8D
  859. {$ENDIF}
  860. CMP EDX,EAX
  861. CMOVG EAX,EDX
  862. CMP ECX,EAX
  863. CMOVL EAX,ECX
  864. {$ENDIF}
  865. end;
  866. //------------------------------------------------------------------------------
  867. //
  868. // Wrap
  869. //
  870. //------------------------------------------------------------------------------
  871. function Wrap(Value, Max: Integer): Integer;
  872. {$IFDEF USENATIVECODE}
  873. begin
  874. Inc(Max);
  875. if (Value < 0) then
  876. Value := Value + Max * (-Value div Max + 1);
  877. Result := Value mod Max;
  878. {$ELSE}
  879. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  880. asm
  881. {$IFDEF TARGET_x64}
  882. MOV EAX,ECX
  883. MOV ECX,R8D
  884. LEA ECX,[RDX+1]
  885. {$ELSE}
  886. LEA ECX,[EDX+1]
  887. {$ENDIF}
  888. CDQ
  889. IDIV ECX
  890. MOV EAX,EDX
  891. TEST EAX,EAX
  892. JNL @Exit
  893. ADD EAX,ECX
  894. @Exit:
  895. {$ENDIF}
  896. end;
  897. //------------------------------------------------------------------------------
  898. function Wrap(Value, Min, Max: Integer): Integer;
  899. var
  900. Range: integer;
  901. begin
  902. Range := Max - Min + 1;
  903. if (Value < Min) then
  904. Value := Value + Range * ((Min - Value) div Range + 1);
  905. Result := Min + (Value - Min) mod Range;
  906. end;
  907. //------------------------------------------------------------------------------
  908. function Wrap(Value, Max: Single): Single;
  909. var
  910. Maxbin: Cardinal absolute Max;
  911. begin
  912. {$if defined(WRAP_USEFLOATMOD)}
  913. if (Maxbin shl 1 <> 0) then // Single=0 test trick
  914. Result := FloatMod(Value, Max)
  915. else
  916. Result := 0;
  917. {$else}
  918. if Max = 0 then
  919. begin
  920. Result := 0;
  921. Exit;
  922. end;
  923. Result := Value;
  924. while Result >= Max do
  925. Result := Result - Max;
  926. while Result < 0 do
  927. Result := Result + Max;
  928. {$ifend}
  929. end;
  930. //------------------------------------------------------------------------------
  931. procedure WrapMem(var Value: Single; Max: Cardinal);
  932. begin
  933. {$if defined(WRAP_USEFLOATMOD)}
  934. if (Max <> 0) then
  935. Value := FloatMod(Value, Max)
  936. else
  937. Value := 0;
  938. {$else}
  939. if Max = 0 then
  940. begin
  941. Value := 0;
  942. Exit;
  943. end;
  944. while Value >= Max do
  945. Value := Value - Max;
  946. while Value < 0 do
  947. Value := Value + Max;
  948. {$ifend}
  949. end;
  950. //------------------------------------------------------------------------------
  951. {$IFDEF USENATIVECODE}
  952. function DivMod(Dividend, Divisor: Integer; out Remainder: Integer): Integer;
  953. begin
  954. Remainder := Dividend mod Divisor;
  955. Result := Dividend div Divisor;
  956. end;
  957. {$ELSE}
  958. function DivMod(Dividend, Divisor: Integer; out Remainder: Integer): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  959. asm
  960. {$IFDEF TARGET_x86}
  961. PUSH EBX
  962. MOV EBX,EDX
  963. CDQ
  964. IDIV EBX
  965. MOV [ECX],EDX
  966. POP EBX
  967. {$ENDIF}
  968. {$IFDEF TARGET_x64}
  969. PUSH RBX
  970. MOV EAX,ECX
  971. MOV ECX,R8D
  972. MOV EBX,EDX
  973. CDQ
  974. IDIV EBX
  975. MOV [RCX],EDX
  976. POP RBX
  977. {$ENDIF}
  978. end;
  979. {$ENDIF}
  980. //------------------------------------------------------------------------------
  981. function WrapPow2(Value, Max: Integer): Integer; overload;
  982. begin
  983. Result := Value and Max;
  984. end;
  985. //------------------------------------------------------------------------------
  986. function WrapPow2(Value, Min, Max: Integer): Integer; overload;
  987. begin
  988. Result := (Value - Min) and (Max - Min) + Min;
  989. end;
  990. //------------------------------------------------------------------------------
  991. function GetOptimalWrap(Max: Integer): TWrapProc; overload;
  992. begin
  993. if (Max >= 0) and IsPowerOf2(Max + 1) then
  994. Result := WrapPow2
  995. else
  996. Result := Wrap;
  997. end;
  998. //------------------------------------------------------------------------------
  999. function GetOptimalWrap(Min, Max: Integer): TWrapProcEx; overload;
  1000. begin
  1001. if (Min >= 0) and (Max >= Min) and IsPowerOf2(Max - Min + 1) then
  1002. Result := WrapPow2
  1003. else
  1004. Result := Wrap;
  1005. end;
  1006. //------------------------------------------------------------------------------
  1007. //
  1008. // Mirror
  1009. //
  1010. //------------------------------------------------------------------------------
  1011. {$IFDEF PUREPASCAL}
  1012. function Mirror(Value, Max: Integer): Integer;
  1013. begin
  1014. if Value >= 0 then
  1015. Result := Value
  1016. else
  1017. Result := -Value;
  1018. while (Result > Max) do
  1019. Result := Abs(Max + Max - Result);
  1020. end;
  1021. {$ELSE}
  1022. // FWIW, there's little, if any, benefit of using the assembler version; The pascal version is just as fast.
  1023. function Mirror(Value, Max: Integer): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1024. asm
  1025. {$IFDEF TARGET_x64}
  1026. MOV EAX, ECX // Value
  1027. {$ENDIF}
  1028. // EAX: Value
  1029. // EDX: Max
  1030. // Max2 := 2*Max
  1031. LEA ECX, [EDX+EDX]
  1032. // Result := Value
  1033. @Loop:
  1034. // Result := Abs(Result)
  1035. TEST EAX, EAX
  1036. JNL @Positive
  1037. NEG EAX
  1038. @Positive:
  1039. // while (Result > Max) do
  1040. CMP EAX, EDX
  1041. JLE @Exit
  1042. // Result := 2*Max - Result
  1043. NEG EAX
  1044. ADD EAX, ECX
  1045. JMP @Loop
  1046. @Exit:
  1047. end;
  1048. {$ENDIF}
  1049. //------------------------------------------------------------------------------
  1050. function Mirror(Value, Min, Max: Integer): Integer;
  1051. begin
  1052. Result := Min + Mirror(Value - Min, Max - Min);
  1053. end;
  1054. //------------------------------------------------------------------------------
  1055. //
  1056. // Reflect
  1057. //
  1058. //------------------------------------------------------------------------------
  1059. function Reflect(Value, Max: Integer): Integer;
  1060. var
  1061. Quotient: Integer;
  1062. begin
  1063. if (Value < 0) then
  1064. begin
  1065. Value := Value - Max;
  1066. Quotient := DivMod(Value, Max + 1, Result);
  1067. Inc(Result, Max);
  1068. end else
  1069. Quotient := DivMod(Value, Max + 1, Result);
  1070. if (Quotient and 1 <> 0) then
  1071. Result := Max - Result;
  1072. end;
  1073. //------------------------------------------------------------------------------
  1074. function Reflect(Value, Min, Max: Integer): Integer;
  1075. var
  1076. Quotient: Integer;
  1077. begin
  1078. if (Value < Min) then
  1079. begin
  1080. Quotient := DivMod(Value - Max, Max - Min + 1, Result);
  1081. Inc(Result, Max);
  1082. end else
  1083. begin
  1084. Quotient := DivMod(Value - Min, Max - Min + 1, Result);
  1085. Inc(Result, Min);
  1086. end;
  1087. if (Quotient and 1 <> 0) then
  1088. Result := Max + Min - Result;
  1089. end;
  1090. //------------------------------------------------------------------------------
  1091. function ReflectPow2(Value, Max: Integer): Integer; overload;
  1092. begin
  1093. if (Value and (Max + 1) = 0) then
  1094. Result := Value and Max
  1095. else
  1096. Result := Max - (Value and Max);
  1097. end;
  1098. //------------------------------------------------------------------------------
  1099. function ReflectPow2(Value, Min, Max: Integer): Integer; overload;
  1100. begin
  1101. Result := ReflectPow2(Value-Min, Max-Min)+Min;
  1102. end;
  1103. //------------------------------------------------------------------------------
  1104. function GetOptimalReflect(Max: Integer): TWrapProc; overload;
  1105. begin
  1106. if (Max >= 0) and IsPowerOf2(Max + 1) then
  1107. Result := ReflectPow2
  1108. else
  1109. Result := Reflect;
  1110. end;
  1111. //------------------------------------------------------------------------------
  1112. function GetOptimalReflect(Min, Max: Integer): TWrapProcEx; overload;
  1113. begin
  1114. if (Min > 0) and (Max >= Min) and IsPowerOf2(Max - Min + 1) then
  1115. Result := ReflectPow2
  1116. else
  1117. Result := Reflect;
  1118. end;
  1119. //------------------------------------------------------------------------------
  1120. //
  1121. // Clamp/Wrap/Mirror
  1122. //
  1123. //------------------------------------------------------------------------------
  1124. function GetWrapProc(WrapMode: TWrapMode): TWrapProc; overload;
  1125. begin
  1126. case WrapMode of
  1127. wmRepeat:
  1128. Result := Wrap;
  1129. wmMirror:
  1130. Result := Mirror;
  1131. {$ifdef GR32_WRAPMODE_REFLECT}
  1132. wmReflect:
  1133. Result := Reflect;
  1134. {$endif}
  1135. else //wmClamp:
  1136. Result := Clamp;
  1137. end;
  1138. end;
  1139. //------------------------------------------------------------------------------
  1140. function GetWrapProc(WrapMode: TWrapMode; Max: Integer): TWrapProc; overload;
  1141. begin
  1142. case WrapMode of
  1143. wmRepeat:
  1144. Result := GetOptimalWrap(Max);
  1145. wmMirror:
  1146. Result := Mirror;
  1147. {$ifdef GR32_WRAPMODE_REFLECT}
  1148. wmReflect:
  1149. Result := GetOptimalReflect(Max);
  1150. {$endif}
  1151. else //wmClamp:
  1152. Result := Clamp;
  1153. end;
  1154. end;
  1155. //------------------------------------------------------------------------------
  1156. function GetWrapProcEx(WrapMode: TWrapMode): TWrapProcEx; overload;
  1157. begin
  1158. case WrapMode of
  1159. wmRepeat:
  1160. Result := Wrap;
  1161. wmMirror:
  1162. Result := Mirror;
  1163. {$ifdef GR32_WRAPMODE_REFLECT}
  1164. wmReflect:
  1165. Result := Reflect;
  1166. {$endif}
  1167. else //wmClamp:
  1168. Result := Clamp;
  1169. end;
  1170. end;
  1171. //------------------------------------------------------------------------------
  1172. function GetWrapProcEx(WrapMode: TWrapMode; Min, Max: Integer): TWrapProcEx; overload;
  1173. begin
  1174. case WrapMode of
  1175. wmRepeat:
  1176. Result := GetOptimalWrap(Min, Max);
  1177. wmMirror:
  1178. Result := Mirror;
  1179. {$ifdef GR32_WRAPMODE_REFLECT}
  1180. wmReflect:
  1181. Result := GetOptimalReflect(Min, Max);
  1182. {$endif}
  1183. else //wmClamp:
  1184. Result := Clamp;
  1185. end;
  1186. end;
  1187. //------------------------------------------------------------------------------
  1188. //
  1189. // Div255: Fast integer division by 255 with limited range
  1190. //
  1191. //------------------------------------------------------------------------------
  1192. function Div255(Value: Word): Word;
  1193. begin
  1194. {$if (defined(FPC)) or ((CompilerVersion >= 36.0) and (defined(TARGET_x86)))}
  1195. // Delphi 12, 32-bit, already knows how to optimize division by 255.
  1196. // Unfortunately it always optimizes as if the argument is a signed 32-bit.
  1197. Result := Value div 255;
  1198. {$else}
  1199. // Input is 16 bit, intermediate result is 32-bit, result is 8 bit
  1200. // Note: Algorithm doesn't take sign into account!
  1201. Result := (Value * $8081) shr 23;
  1202. {$ifend}
  1203. end;
  1204. //------------------------------------------------------------------------------
  1205. function FastDiv255(Value: Word): Word;
  1206. begin
  1207. // Input is 16 bit, intermediate result is 32-bit (25 used), result is 8 bit
  1208. // Note: Algorithm doesn't take sign into account!
  1209. Result := (Value + ((Value + 257) shr 8)) shr 8;
  1210. end;
  1211. //------------------------------------------------------------------------------
  1212. function Div255Round(Value: Word): Word;
  1213. begin
  1214. // Input is 16 bit, intermediate result is 24-bit, result is 8 bit
  1215. // Note: Algorithm doesn't take sign into account!
  1216. Result := ((Value + 128) * 257) shr 16;
  1217. end;
  1218. //------------------------------------------------------------------------------
  1219. //
  1220. // FastRound
  1221. //
  1222. //------------------------------------------------------------------------------
  1223. //------------------------------------------------------------------------------
  1224. // FastRound_Pas
  1225. //------------------------------------------------------------------------------
  1226. function FastRound_Pas(Value: TFloat): Integer;
  1227. begin
  1228. Result := Round(Value);
  1229. end;
  1230. {$IFNDEF PUREPASCAL}
  1231. //------------------------------------------------------------------------------
  1232. // FastRound_SSE41
  1233. //------------------------------------------------------------------------------
  1234. function FastRound_SSE41(Value: TFloat): Integer; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64} nostackframe; {$ENDIF}{$ENDIF}
  1235. asm
  1236. {$if defined(TARGET_x86)}
  1237. MOVSS xmm0, Value
  1238. {$ifend}
  1239. ROUNDSS xmm0, xmm0, SSE_ROUND.TO_NEAREST_INT + SSE_ROUND.NO_EXC
  1240. CVTSS2SI eax, xmm0
  1241. end;
  1242. {$ENDIF}
  1243. //------------------------------------------------------------------------------
  1244. //
  1245. // FastTrunc
  1246. //
  1247. //------------------------------------------------------------------------------
  1248. //------------------------------------------------------------------------------
  1249. // FastTrunc_Pas
  1250. //------------------------------------------------------------------------------
  1251. //
  1252. // Just defer to RTL Trunc
  1253. //
  1254. function FastTrunc_Pas(Value: TFloat): Integer;
  1255. begin
  1256. Result := Trunc(Value);
  1257. end;
  1258. {$IFNDEF PUREPASCAL}
  1259. //------------------------------------------------------------------------------
  1260. // FastTrunc_SSE2
  1261. //------------------------------------------------------------------------------
  1262. //
  1263. // Faster that RTL Trunc on x86 and x64
  1264. //
  1265. {$IFNDEF OMIT_SSE2}
  1266. function FastTrunc_SSE2(Value: TFloat): Integer; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64} nostackframe; {$ENDIF}{$ENDIF}
  1267. asm
  1268. {$if defined(TARGET_x86)}
  1269. MOVSS XMM0, Value
  1270. {$ifend}
  1271. CVTTSS2SI EAX, XMM0
  1272. end;
  1273. {$ENDIF}
  1274. //------------------------------------------------------------------------------
  1275. // SlowTrunc_SSE2
  1276. //------------------------------------------------------------------------------
  1277. //
  1278. // Faster that RTL Trunc on x64 (and sometimes on x86).
  1279. //
  1280. {$IFNDEF OMIT_SSE2}
  1281. function SlowTrunc_SSE2(Value: TFloat): Integer; {$IFDEF FPC} assembler; {$ENDIF}
  1282. var
  1283. SaveMXCSR: Cardinal;
  1284. NewMXCSR: Cardinal;
  1285. asm
  1286. XOR ECX, ECX
  1287. // Save current rounding mode
  1288. STMXCSR SaveMXCSR
  1289. // Load rounding mode
  1290. MOV EAX, SaveMXCSR
  1291. // Do we need to change anything?
  1292. MOV ECX, EAX
  1293. NOT ECX
  1294. AND ECX, MXCSR.TRUNC
  1295. JZ @SkipSetMXCSR // Skip expensive LDMXCSR
  1296. @SetMXCSR:
  1297. // Save current rounding mode in ECX and flag that we need to restore it
  1298. MOV ECX, EAX
  1299. // Set rounding mode to truncation
  1300. AND EAX, MXCSR.MASK
  1301. OR EAX, MXCSR.TRUNC
  1302. // Set new rounding mode
  1303. MOV NewMXCSR, EAX
  1304. LDMXCSR NewMXCSR
  1305. @SkipSetMXCSR:
  1306. {$if defined(TARGET_x86)}
  1307. MOVSS XMM0, Value
  1308. {$ifend}
  1309. // Round/Trunc
  1310. CVTSS2SI EAX, XMM0
  1311. // Restore rounding mode
  1312. // Did we modify it?
  1313. TEST ECX, ECX
  1314. JZ @SkipRestoreMXCSR // Skip expensive LDMXCSR
  1315. // Restore old rounding mode
  1316. LDMXCSR SaveMXCSR
  1317. @SkipRestoreMXCSR:
  1318. end;
  1319. {$ENDIF}
  1320. //------------------------------------------------------------------------------
  1321. // FastTrunc_SSE41
  1322. //------------------------------------------------------------------------------
  1323. //
  1324. // Faster that RTL Trunc on x86
  1325. //
  1326. {$IFNDEF OMIT_SSE2}
  1327. function FastTrunc_SSE41(Value: TFloat): Integer; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64} nostackframe; {$ENDIF}{$ENDIF}
  1328. asm
  1329. {$if defined(TARGET_x86)}
  1330. MOVSS xmm0, Value
  1331. {$ifend}
  1332. ROUNDSS xmm0, xmm0, SSE_ROUND.TO_ZERO + SSE_ROUND.NO_EXC
  1333. CVTSS2SI eax, xmm0
  1334. end;
  1335. {$ENDIF}
  1336. {$ENDIF}
  1337. //------------------------------------------------------------------------------
  1338. //
  1339. // FastFloor
  1340. //
  1341. //------------------------------------------------------------------------------
  1342. function FastFloor(Value: TFloat): Integer;
  1343. begin
  1344. Result := FastFloorSingle(Value);
  1345. end;
  1346. function FastFloor(Value: Double): Integer;
  1347. begin
  1348. Result := FastFloorDouble(Value);
  1349. end;
  1350. //------------------------------------------------------------------------------
  1351. // FastFloorSingle_Pas
  1352. //------------------------------------------------------------------------------
  1353. function FastFloorSingle_Pas(Value: TFloat): Integer;
  1354. begin
  1355. Result := Integer(Trunc(Value));
  1356. if Frac(Value) < 0 then
  1357. Dec(Result);
  1358. end;
  1359. //------------------------------------------------------------------------------
  1360. // FastFloorDouble_Pas
  1361. //------------------------------------------------------------------------------
  1362. function FastFloorDouble_Pas(Value: Double): Integer;
  1363. begin
  1364. Result := Integer(Trunc(Value));
  1365. if Frac(Value) < 0 then
  1366. Dec(Result);
  1367. end;
  1368. {$IFNDEF PUREPASCAL}
  1369. //------------------------------------------------------------------------------
  1370. // FastFloorSingle_SSE41
  1371. //------------------------------------------------------------------------------
  1372. {$IFNDEF OMIT_SSE2}
  1373. function FastFloorSingle_SSE41(Value: Single): Integer; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64} nostackframe; {$ENDIF}{$ENDIF}
  1374. asm
  1375. {$if defined(TARGET_x86)}
  1376. MOVSS xmm0, Value
  1377. {$ifend}
  1378. ROUNDSS xmm0, xmm0, SSE_ROUND.TO_NEG_INF + SSE_ROUND.NO_EXC
  1379. CVTSS2SI eax, xmm0
  1380. end;
  1381. {$ENDIF}
  1382. //------------------------------------------------------------------------------
  1383. // FastFloorDouble_SSE41
  1384. //------------------------------------------------------------------------------
  1385. {$IFNDEF OMIT_SSE2}
  1386. function FastFloorDouble_SSE41(Value: Double): Integer; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64} nostackframe; {$ENDIF}{$ENDIF}
  1387. asm
  1388. {$if defined(TARGET_x86)}
  1389. MOVSD xmm0, Value
  1390. {$ifend}
  1391. ROUNDSD xmm0, xmm0, SSE_ROUND.TO_NEG_INF + SSE_ROUND.NO_EXC
  1392. CVTTSD2SI eax, xmm0
  1393. end;
  1394. {$ENDIF}
  1395. {$ENDIF}
  1396. //------------------------------------------------------------------------------
  1397. //
  1398. // FastCeil
  1399. //
  1400. //------------------------------------------------------------------------------
  1401. function FastCeil(Value: TFloat): Integer;
  1402. begin
  1403. Result := FastCeilSingle(Value);
  1404. end;
  1405. function FastCeil(Value: Double): Integer;
  1406. begin
  1407. Result := FastCeilDouble(Value);
  1408. end;
  1409. //------------------------------------------------------------------------------
  1410. // FastCeilSingle_Pas
  1411. //------------------------------------------------------------------------------
  1412. function FastCeilSingle_Pas(Value: TFloat): Integer;
  1413. begin
  1414. Result := Integer(Trunc(Value));
  1415. if Frac(Value) > 0 then
  1416. Inc(Result);
  1417. end;
  1418. //------------------------------------------------------------------------------
  1419. // FastCeilDouble_Pas
  1420. //------------------------------------------------------------------------------
  1421. function FastCeilDouble_Pas(Value: Double): Integer;
  1422. begin
  1423. Result := Integer(Trunc(Value));
  1424. if Frac(Value) > 0 then
  1425. Inc(Result);
  1426. end;
  1427. {$IFNDEF PUREPASCAL}
  1428. //------------------------------------------------------------------------------
  1429. // FastCeilSingle_SSE41
  1430. //------------------------------------------------------------------------------
  1431. function FastCeilSingle_SSE41(Value: Single): Integer; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64} nostackframe; {$ENDIF}{$ENDIF}
  1432. asm
  1433. {$if defined(TARGET_x86)}
  1434. MOVSS xmm0, Value
  1435. {$ifend}
  1436. ROUNDSS xmm0, xmm0, SSE_ROUND.TO_POS_INF + SSE_ROUND.NO_EXC
  1437. CVTSS2SI eax, xmm0
  1438. end;
  1439. //------------------------------------------------------------------------------
  1440. // FastCeilDouble_SSE41
  1441. //------------------------------------------------------------------------------
  1442. function FastCeilDouble_SSE41(Value: Double): Integer; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64} nostackframe; {$ENDIF}{$ENDIF}
  1443. asm
  1444. {$if defined(TARGET_x86)}
  1445. MOVSD xmm0, Value
  1446. {$ifend}
  1447. ROUNDSD xmm0, xmm0, SSE_ROUND.TO_POS_INF + SSE_ROUND.NO_EXC
  1448. CVTTSD2SI eax, xmm0
  1449. end;
  1450. {$ENDIF}
  1451. //------------------------------------------------------------------------------
  1452. //
  1453. // SAR: Shift right with sign conservation
  1454. //
  1455. //------------------------------------------------------------------------------
  1456. function SAR_3(Value: Integer): Integer;
  1457. {$IFDEF PUREPASCAL}
  1458. begin
  1459. Result := Value div 8;
  1460. {$ELSE}
  1461. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1462. asm
  1463. {$IFDEF TARGET_x64}
  1464. MOV EAX,ECX
  1465. {$ENDIF}
  1466. SAR EAX,3
  1467. {$ENDIF}
  1468. end;
  1469. //------------------------------------------------------------------------------
  1470. function SAR_4(Value: Integer): Integer;
  1471. {$IFDEF PUREPASCAL}
  1472. begin
  1473. Result := Value div 16;
  1474. {$ELSE}
  1475. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1476. asm
  1477. {$IFDEF TARGET_x64}
  1478. MOV EAX,ECX
  1479. {$ENDIF}
  1480. SAR EAX,4
  1481. {$ENDIF}
  1482. end;
  1483. //------------------------------------------------------------------------------
  1484. function SAR_6(Value: Integer): Integer;
  1485. {$IFDEF PUREPASCAL}
  1486. begin
  1487. Result := Value div 64;
  1488. {$ELSE}
  1489. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1490. asm
  1491. {$IFDEF TARGET_x64}
  1492. MOV EAX,ECX
  1493. {$ENDIF}
  1494. SAR EAX,6
  1495. {$ENDIF}
  1496. end;
  1497. //------------------------------------------------------------------------------
  1498. function SAR_8(Value: Integer): Integer;
  1499. {$IFDEF PUREPASCAL}
  1500. begin
  1501. Result := Value div 256;
  1502. {$ELSE}
  1503. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1504. asm
  1505. {$IFDEF TARGET_x64}
  1506. MOV EAX,ECX
  1507. {$ENDIF}
  1508. SAR EAX,8
  1509. {$ENDIF}
  1510. end;
  1511. //------------------------------------------------------------------------------
  1512. function SAR_9(Value: Integer): Integer;
  1513. {$IFDEF PUREPASCAL}
  1514. begin
  1515. Result := Value div 512;
  1516. {$ELSE}
  1517. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1518. asm
  1519. {$IFDEF TARGET_x64}
  1520. MOV EAX,ECX
  1521. {$ENDIF}
  1522. SAR EAX,9
  1523. {$ENDIF}
  1524. end;
  1525. //------------------------------------------------------------------------------
  1526. function SAR_11(Value: Integer): Integer;
  1527. {$IFDEF PUREPASCAL}
  1528. begin
  1529. Result := Value div 2048;
  1530. {$ELSE}
  1531. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1532. asm
  1533. {$IFDEF TARGET_x64}
  1534. MOV EAX,ECX
  1535. {$ENDIF}
  1536. SAR EAX,11
  1537. {$ENDIF}
  1538. end;
  1539. //------------------------------------------------------------------------------
  1540. function SAR_12(Value: Integer): Integer;
  1541. {$IFDEF PUREPASCAL}
  1542. begin
  1543. Result := Value div 4096;
  1544. {$ELSE}
  1545. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1546. asm
  1547. {$IFDEF TARGET_x64}
  1548. MOV EAX,ECX
  1549. {$ENDIF}
  1550. SAR EAX,12
  1551. {$ENDIF}
  1552. end;
  1553. //------------------------------------------------------------------------------
  1554. function SAR_13(Value: Integer): Integer;
  1555. {$IFDEF PUREPASCAL}
  1556. begin
  1557. Result := Value div 8192;
  1558. {$ELSE}
  1559. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1560. asm
  1561. {$IFDEF TARGET_x64}
  1562. MOV EAX,ECX
  1563. {$ENDIF}
  1564. SAR EAX,13
  1565. {$ENDIF}
  1566. end;
  1567. //------------------------------------------------------------------------------
  1568. function SAR_14(Value: Integer): Integer;
  1569. {$IFDEF PUREPASCAL}
  1570. begin
  1571. Result := Value div 16384;
  1572. {$ELSE}
  1573. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1574. asm
  1575. {$IFDEF TARGET_x64}
  1576. MOV EAX,ECX
  1577. {$ENDIF}
  1578. SAR EAX,14
  1579. {$ENDIF}
  1580. end;
  1581. //------------------------------------------------------------------------------
  1582. function SAR_15(Value: Integer): Integer;
  1583. {$IFDEF PUREPASCAL}
  1584. begin
  1585. Result := Value div 32768;
  1586. {$ELSE}
  1587. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1588. asm
  1589. {$IFDEF TARGET_x64}
  1590. MOV EAX,ECX
  1591. {$ENDIF}
  1592. SAR EAX,15
  1593. {$ENDIF}
  1594. end;
  1595. //------------------------------------------------------------------------------
  1596. function SAR_16(Value: Integer): Integer;
  1597. {$IFDEF PUREPASCAL}
  1598. begin
  1599. Result := Value div 65536;
  1600. {$ELSE}
  1601. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1602. asm
  1603. {$IFDEF TARGET_x64}
  1604. MOV EAX,ECX
  1605. {$ENDIF}
  1606. SAR EAX,16
  1607. {$ENDIF}
  1608. end;
  1609. //------------------------------------------------------------------------------
  1610. //
  1611. // ColorSwap
  1612. //
  1613. //------------------------------------------------------------------------------
  1614. function ColorSwap(WinColor: TColor): TColor32;
  1615. {$IFDEF USENATIVECODE}
  1616. var
  1617. WCEn: TColor32Entry absolute WinColor;
  1618. REn : TColor32Entry absolute Result;
  1619. begin
  1620. Result := WCEn.ARGB;
  1621. REn.A := $FF;
  1622. REn.R := WCEn.B;
  1623. REn.B := WCEn.R;
  1624. {$ELSE}
  1625. {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1626. asm
  1627. // EAX = WinColor
  1628. // this function swaps R and B bytes in ABGR
  1629. // and writes $FF into A component
  1630. {$IFDEF TARGET_x64}
  1631. MOV EAX,ECX
  1632. {$ENDIF}
  1633. BSWAP EAX
  1634. MOV AL, $FF
  1635. ROR EAX,8
  1636. {$ENDIF}
  1637. end;
  1638. //------------------------------------------------------------------------------
  1639. //
  1640. // StackAlloc
  1641. //
  1642. //------------------------------------------------------------------------------
  1643. {$IFDEF USESTACKALLOC}
  1644. {$IFDEF PUREPASCAL}
  1645. function StackAlloc(Size: Integer): Pointer;
  1646. begin
  1647. GetMem(Result, Size);
  1648. end;
  1649. procedure StackFree(P: Pointer);
  1650. begin
  1651. FreeMem(P);
  1652. end;
  1653. {$ELSE}
  1654. { StackAlloc allocates a 'small' block of memory from the stack by
  1655. decrementing SP. This provides the allocation speed of a local variable,
  1656. but the runtime size flexibility of heap allocated memory.
  1657. x64 implementation by Jameel Halabi
  1658. }
  1659. function StackAlloc(Size: Integer): Pointer; register; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1660. asm
  1661. {$IFDEF TARGET_x86}
  1662. POP ECX // return address
  1663. MOV EDX, ESP
  1664. ADD EAX, 3
  1665. AND EAX, not 3 // round up to keep ESP dword aligned
  1666. CMP EAX, 4092
  1667. JLE @@2
  1668. @@1:
  1669. SUB ESP, 4092
  1670. PUSH EAX // make sure we touch guard page, to grow stack
  1671. SUB EAX, 4096
  1672. JNS @@1
  1673. ADD EAX, 4096
  1674. @@2:
  1675. SUB ESP, EAX
  1676. MOV EAX, ESP // function result = low memory address of block
  1677. PUSH EDX // save original SP, for cleanup
  1678. MOV EDX, ESP
  1679. SUB EDX, 4
  1680. PUSH EDX // save current SP, for sanity check (sp = [sp])
  1681. PUSH ECX // return to caller
  1682. {$ENDIF}
  1683. {$IFDEF TARGET_x64}
  1684. {$IFNDEF FPC}
  1685. .NOFRAME
  1686. {$ENDIF}
  1687. POP R8 // return address
  1688. MOV RDX, RSP // original SP
  1689. ADD ECX, 15
  1690. AND ECX, NOT 15 // round up to keep SP dqword aligned
  1691. CMP ECX, 4088
  1692. JLE @@2
  1693. @@1:
  1694. SUB RSP, 4088
  1695. PUSH RCX // make sure we touch guard page, to grow stack
  1696. SUB ECX, 4096
  1697. JNS @@1
  1698. ADD ECX, 4096
  1699. @@2:
  1700. SUB RSP, RCX
  1701. MOV RAX, RSP // function result = low memory address of block
  1702. PUSH RDX // save original SP, for cleanup
  1703. MOV RDX, RSP
  1704. SUB RDX, 8
  1705. PUSH RDX // save current SP, for sanity check (sp = [sp])
  1706. PUSH R8 // return to caller
  1707. {$ENDIF}
  1708. end;
  1709. { StackFree pops the memory allocated by StackAlloc off the stack.
  1710. - Calling StackFree is optional - SP will be restored when the calling routine
  1711. exits, but it's a good idea to free the stack allocated memory ASAP anyway.
  1712. - StackFree must be called in the same stack context as StackAlloc - not in
  1713. a subroutine or finally block.
  1714. - Multiple StackFree calls must occur in reverse order of their corresponding
  1715. StackAlloc calls.
  1716. - Built-in sanity checks guarantee that an improper call to StackFree will not
  1717. corrupt the stack. Worst case is that the stack block is not released until
  1718. the calling routine exits. }
  1719. procedure StackFree(P: Pointer); register; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
  1720. asm
  1721. {$IFDEF TARGET_x86}
  1722. POP ECX // return address
  1723. MOV EDX, DWORD PTR [ESP]
  1724. SUB EAX, 8
  1725. CMP EDX, ESP // sanity check #1 (SP = [SP])
  1726. JNE @Exit
  1727. CMP EDX, EAX // sanity check #2 (P = this stack block)
  1728. JNE @Exit
  1729. MOV ESP, DWORD PTR [ESP+4] // restore previous SP
  1730. @Exit:
  1731. PUSH ECX // return to caller
  1732. {$ENDIF}
  1733. {$IFDEF TARGET_x64}
  1734. {$IFNDEF FPC}
  1735. .NOFRAME
  1736. {$ENDIF}
  1737. POP R8 // return address
  1738. MOV RDX, QWORD PTR [RSP]
  1739. SUB RCX, 16
  1740. CMP RDX, RSP // sanity check #1 (SP = [SP])
  1741. JNE @Exit
  1742. CMP RDX, RCX // sanity check #2 (P = this stack block)
  1743. JNE @Exit
  1744. MOV RSP, QWORD PTR [RSP + 8] // restore previous SP
  1745. @Exit:
  1746. PUSH R8 // return to caller
  1747. {$ENDIF}
  1748. end;
  1749. {$ENDIF}
  1750. {$ENDIF}
  1751. //------------------------------------------------------------------------------
  1752. //
  1753. // Bindings
  1754. //
  1755. //------------------------------------------------------------------------------
  1756. procedure RegisterBindings;
  1757. begin
  1758. {$WARN SYMBOL_EXPERIMENTAL OFF}
  1759. LowLevelRegistry := NewRegistry('GR32_LowLevel bindings');
  1760. LowLevelRegistry.RegisterBinding(FID_FILLLONGWORD, @@FillLongWord, 'FillLongWord');
  1761. LowLevelRegistry.RegisterBinding(FID_FAST_TRUNC, @@FastTrunc, 'FastTrunc');
  1762. LowLevelRegistry.RegisterBinding(FID_FAST_ROUND, @@FastRound, 'FastRound');
  1763. LowLevelRegistry.RegisterBinding(@@FastFloorSingle, 'FastFloorSingle');
  1764. LowLevelRegistry.RegisterBinding(@@FastFloorDouble, 'FastFloorDouble');
  1765. LowLevelRegistry.RegisterBinding(@@FastCeilSingle, 'FastCeilSingle');
  1766. LowLevelRegistry.RegisterBinding(@@FastCeilDouble, 'FastCeilDouble');
  1767. LowLevelRegistry[@@FillLongWord].Add( @FillLongWord_Pas, [isPascal]).Name := 'FillLongWord_Pas';
  1768. LowLevelRegistry[@@FastTrunc].Add( @FastTrunc_Pas, [isPascal]).Name := 'FastTrunc_Pas';
  1769. LowLevelRegistry[@@FastRound].Add( @FastRound_Pas, [isPascal]).Name := 'FastRound_Pas';
  1770. LowLevelRegistry[@@FastFloorSingle].Add( @FastFloorSingle_Pas, [isPascal]).Name := 'FastFloorSingle_Pas';
  1771. LowLevelRegistry[@@FastFloorDouble].Add( @FastFloorDouble_Pas, [isPascal]).Name := 'FastFloorDouble_Pas';
  1772. LowLevelRegistry[@@FastCeilSingle].Add( @FastCeilSingle_Pas, [isPascal]).Name := 'FastCeilSingle_Pas';
  1773. LowLevelRegistry[@@FastCeilDouble].Add( @FastCeilDouble_Pas, [isPascal]).Name := 'FastCeilDouble_Pas';
  1774. {$if (not defined(PUREPASCAL))}
  1775. LowLevelRegistry[@@FillLongWord].Add( @FillLongWord_ASM, [isAssembler]).Name := 'FillLongWord_ASM';
  1776. {$ifend}
  1777. {$if (not defined(PUREPASCAL)) and (not defined(OMIT_SSE2))}
  1778. LowLevelRegistry[@@FillLongWord].Add( @FillLongword_SSE2, [isSSE2]).Name := 'FillLongword_SSE2';
  1779. LowLevelRegistry[@@FastTrunc].Add( @FastTrunc_SSE2, [isSSE2]).Name := 'FastTrunc_SSE2';
  1780. LowLevelRegistry[@@FastRound].Add( @FastRound_SSE41, [isSSE41]).Name := 'FastRound_SSE41';
  1781. LowLevelRegistry[@@FastFloorSingle].Add( @FastFloorSingle_SSE41, [isSSE41]).Name := 'FastFloorSingle_SSE41';
  1782. LowLevelRegistry[@@FastFloorDouble].Add( @FastFloorDouble_SSE41, [isSSE41]).Name := 'FastFloorDouble_SSE41';
  1783. LowLevelRegistry[@@FastCeilSingle].Add( @FastCeilSingle_SSE41, [isSSE41]).Name := 'FastCeilSingle_SSE41';
  1784. LowLevelRegistry[@@FastCeilDouble].Add( @FastCeilDouble_SSE41, [isSSE41]).Name := 'FastCeilDouble_SSE41';
  1785. {$if defined(BENCHMARK)}
  1786. LowLevelRegistry[@@FastTrunc].Add( @SlowTrunc_SSE2, [isSSE2], BindingPriorityWorse).Name := 'SlowTrunc_SSE2';
  1787. {$ifend}
  1788. {$ifend}
  1789. {$if defined(BENCHMARK)}
  1790. LowLevelRegistry[@@FastFloorSingle].Add( @System.Math.Floor, [isReference], BindingPriorityWorse).Name := 'Math.Floor';
  1791. {$ifend}
  1792. LowLevelRegistry.RebindAll;
  1793. end;
  1794. //------------------------------------------------------------------------------
  1795. //------------------------------------------------------------------------------
  1796. //------------------------------------------------------------------------------
  1797. initialization
  1798. RegisterBindings;
  1799. end.