impl.inc 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900
  1. /**--------------------------------------------------------------------------**\
  2. ===================================
  3. y_inline - PAWN inline functions.
  4. ===================================
  5. Description:
  6. This library allows a user to write inline functions in their script. It
  7. first detects all inline functions and generates data on them, such as
  8. parameter counts and addresses. When an inline function is passed in code
  9. its current context data is stored. Finally, when an inline function is
  10. found to be called at some point its current local stack is stored in global
  11. memory. When the function actually is called, the stack is restored, and
  12. additional parameters which are the inline function parameters, are passed.
  13. Legal:
  14. Version: MPL 1.1
  15. The contents of this file are subject to the Mozilla Public License Version
  16. 1.1 (the "License"); you may not use this file except in compliance with
  17. the License. You may obtain a copy of the License at
  18. http://www.mozilla.org/MPL/
  19. Software distributed under the License is distributed on an "AS IS" basis,
  20. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  21. for the specific language governing rights and limitations under the
  22. License.
  23. The Original Code is the YSI Inline Function include.
  24. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  25. Portions created by the Initial Developer are Copyright (C) 2011
  26. the Initial Developer. All Rights Reserved.
  27. Contributors:
  28. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  29. Thanks:
  30. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  31. ZeeX - Very productive conversations.
  32. koolk - IsPlayerinAreaEx code.
  33. TheAlpha - Danish translation.
  34. breadfish - German translation.
  35. Fireburn - Dutch translation.
  36. yom - French translation.
  37. 50p - Polish translation.
  38. Zamaroht - Spanish translation.
  39. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  40. for me to strive to better.
  41. Pixels^ - Running XScripters where the idea was born.
  42. Matite - Pestering me to release it and using it.
  43. Very special thanks to:
  44. Thiadmer - PAWN, whose limits continue to amaze me!
  45. Kye/Kalcor - SA:MP.
  46. SA:MP Team past, present and future - SA:MP.
  47. Version:
  48. 1.0
  49. Changelog:
  50. 22/06/13:
  51. Rewrote the library from scratch for better performance all round.
  52. 20/10/12:
  53. Fixed a bug with "Callback_Release" with public functions.
  54. 15/11/11:
  55. Changed the precedence of "using" types.
  56. 19/09/11:
  57. First version
  58. \**--------------------------------------------------------------------------**/
  59. /*
  60. ad88888ba
  61. d8" "8b ,d
  62. Y8, 88
  63. `Y8aaaaa, ,adPPYba, MM88MMM 88 88 8b,dPPYba,
  64. `"""""8b, a8P_____88 88 88 88 88P' "8a
  65. `8b 8PP""""""" 88 88 88 88 d8
  66. Y8a a8P "8b, ,aa 88, "8a, ,a88 88b, ,a8"
  67. "Y88888P" `"Ybbd8"' "Y888 `"YbbdP'Y8 88`YbbdP"'
  68. 88
  69. 88
  70. */
  71. #define INLINE_TYPE_VAR (0b00)
  72. #define INLINE_TYPE_REF (0b10)
  73. #define INLINE_TYPE_STR (0b01)
  74. #define INLINE_TYPE_ARR (0b11)
  75. enum E_CALLBACK_DATA
  76. {
  77. // ===== ALWAYS FIRST =====
  78. ResolvedAlloc:E_CALLBACK_DATA_ALLOC = 0, // Fully resolved memory address.
  79. // ===== ALWAYS SECOND =====
  80. Function:E_CALLBACK_DATA_POINTER = 1, // Inline entry point.
  81. // ===== ALWAYS AFTER =====
  82. E_CALLBACK_DATA_OFFSET, // Local variables size.
  83. E_CALLBACK_DATA_FORMAT[2]
  84. }
  85. static stock
  86. YSI_g_sRemoteFunctions = -1, // Pointer to the remote function stubs list.
  87. YSI_g_sRemoteSpecifiers = -1, // Pointer to the remote function handlers.
  88. YSI_g_sRemoteStub = -1, // The absolute address of the remote call stub.
  89. YSI_g_sRemoteStringsStart = -1,
  90. YSI_g_sRemoteStringsEnd = -1,
  91. YSI_g_sPrevInlineFunc = 0,
  92. YSI_g_sPrevJumpOver = 0,
  93. YSI_g_sCurInlineLocals, // Number of locals in current parent.
  94. YSI_g_sCurInlineParams, // Number of parameters to current parent.
  95. YSI_g_sCurInlinePointer, // Storage for the inline function's return.
  96. YSI_g_sCurInlineEntry, // Pointer to the start of the data segment.
  97. YSI_g_sCurInlineLoop, // Pointer to the current loopback label.
  98. YSI_g_sCurInlineCode; // Pointer to the start of user code.
  99. static stock const
  100. YSI_g_scError[] = "\7\7\7*** YSI Error: Unrecognised compilation in y_inline.";
  101. // Operators for doing "return" from inside an inline function.
  102. #define inline_return YSI_gInlineRet+=
  103. #define @return inline_return
  104. stock
  105. InlineRet:YSI_gInlineRet;
  106. /**--------------------------------------------------------------------------**\
  107. <summary>_@_y_inline_@_</summary>
  108. <returns>
  109. -
  110. </returns>
  111. <remarks>
  112. Calls functions we call via "SYSREQ.C".
  113. </remarks>
  114. \**--------------------------------------------------------------------------**/
  115. forward _@_y_inline_@_();
  116. public _@_y_inline_@_()
  117. {
  118. strpack("", "");
  119. strcat("", "");
  120. strcmp("", "");
  121. strfind("", NULL);
  122. CallRemoteFunction("", "");
  123. }
  124. /**--------------------------------------------------------------------------**\
  125. <summary>Inline_DecodeSimple</summary>
  126. <param name="from[]">Array of variable types.</param>
  127. <param name="at">Type slot.</param>
  128. <returns>
  129. The next variable type stored in the bit array.
  130. </returns>
  131. <remarks>
  132. Returns data from a bit array when it is known that only basic types are
  133. stored (i.e. no arrays with length parameters).
  134. </remarks>
  135. \**--------------------------------------------------------------------------**/
  136. #define Inline_DecodeSimple(%0,%1) (I@ = %0[(%1) >>> 5] & (2 << ((%1) & 0x1F)), (%1) += 2, I@)
  137. // This used to be a compile-time macro to convert its parameters to the
  138. // compressed format - it isn't anymore. I tried to figure out if it was
  139. // possible, and it probably is, but VERY hard, even compared to other macros
  140. // I've written.
  141. #define _F<%0> (#%0)
  142. /*
  143. 88b d88 88 db 88888888ba 88
  144. 888b d888 "" d88b 88 "8b 88
  145. 88`8b d8'88 d8'`8b 88 ,8P 88
  146. 88 `8b d8' 88 ,adPPYYba, 88 8b,dPPYba, d8' `8b 88aaaaaa8P' 88
  147. 88 `8b d8' 88 "" `Y8 88 88P' `"8a d8YaaaaY8b 88""""""' 88
  148. 88 `8b d8' 88 ,adPPPPP88 88 88 88 d8""""""""8b 88 88
  149. 88 `888' 88 88, ,88 88 88 88 d8' `8b 88 88
  150. 88 `8' 88 `"8bbdP"Y8 88 88 88 d8' `8b 88 88
  151. */
  152. stock Inline_Reset(callback[E_CALLBACK_DATA])
  153. {
  154. return
  155. callback[E_CALLBACK_DATA_ALLOC] = ResolvedAlloc:0,
  156. callback[E_CALLBACK_DATA_POINTER] = Function:0,
  157. callback[E_CALLBACK_DATA_OFFSET] = 0,
  158. callback[E_CALLBACK_DATA_FORMAT] = 0,
  159. callback[E_CALLBACK_DATA_FORMAT + E_CALLBACK_DATA:1] = 0,
  160. 0;
  161. }
  162. /**--------------------------------------------------------------------------**\
  163. <summary>GetRemoteFunction</summary>
  164. <param name="func[]">Public function to get.</param>
  165. <param name="spec[]">The structure of the function's parameters.</param>
  166. <returns>
  167. A pointer to the function.
  168. </returns>
  169. <remarks>
  170. Accepts the following parameter specifiers:
  171. i - Integer (also x/c/d/h)
  172. f - Float (also g)
  173. s - String
  174. ai - Array (followed by length)
  175. v - Reference (&var, any tag)
  176. </remarks>
  177. \**--------------------------------------------------------------------------**/
  178. stock Function:GetRemoteFunction(const func[], const spec[])
  179. {
  180. if (YSI_g_sRemoteStub == -1)
  181. {
  182. // In this case everything is very simple because we know there can't be
  183. // any remote functions found so we can just make them instead of
  184. // wasting time searching.
  185. P:C(if (YSI_g_sRemoteFunctions != -1 || YSI_g_sRemoteSpecifiers != -1) P:E("Some, but not all, remote handlers installed."););
  186. // There are no handlers, so there is no stub written.
  187. YSI_g_sRemoteStub = Remote_WriteStubCode(),
  188. Remote_WriteJustSpec(ref(YSI_g_sRemoteSpecifiers), spec);
  189. return Function:Remote_WriteSpecAndFunc(ref(YSI_g_sRemoteFunctions), func, YSI_g_sRemoteSpecifiers, spec);
  190. }
  191. new
  192. fmatPtr = Remote_DoSearch(spec, YSI_g_sRemoteSpecifiers);
  193. if (fmatPtr == -1)
  194. {
  195. Remote_WriteJustSpec(ref(YSI_g_sRemoteSpecifiers), spec),
  196. fmatPtr = YSI_g_sRemoteSpecifiers;
  197. }
  198. else
  199. {
  200. // Found the specifier already defined, is this function already defined
  201. // for this specifier?
  202. new
  203. tmpPtr = AMX_Read(fmatPtr + 4),
  204. funcPtr = Remote_DoSearch(func, YSI_g_sRemoteFunctions);
  205. while (funcPtr != -1)
  206. {
  207. // Check if this instance of the function matches the specifier.
  208. if (AMX_Read(funcPtr + 2 * 4) == tmpPtr) return Function:(funcPtr + 3 * 4 - AMX_HEADER_COD);
  209. else funcPtr = Remote_DoSearch(func, AMX_Read(funcPtr));
  210. }
  211. }
  212. return Function:Remote_WriteSpecAndFunc(ref(YSI_g_sRemoteFunctions), func, fmatPtr, spec);
  213. }
  214. /**--------------------------------------------------------------------------**\
  215. <summary>GetLocalFunction</summary>
  216. <param name="func[]">Public function to get.</param>
  217. <param name="spec[]">The structure of the function's parameters.</param>
  218. <returns>
  219. A pointer to the function.
  220. </returns>
  221. <remarks>
  222. Accepts the following parameter specifiers:
  223. i - Integer (also x/c/d/h)
  224. f - Float (also g)
  225. s - String
  226. ai - Array (followed by length)
  227. v - Reference (&var, any tag)
  228. </remarks>
  229. \**--------------------------------------------------------------------------**/
  230. stock Function:GetLocalFunction(const func[], const spec[])
  231. {
  232. // Get the function pointer.
  233. new
  234. fptr = funcidx(func);
  235. // Find the first match.
  236. if (fptr == -1) return Function:0;
  237. return Function:StoredF_WritePublicCode(fptr, spec);
  238. }
  239. /**--------------------------------------------------------------------------**\
  240. <summary>CallStoredFunction</summary>
  241. <param name="func">Function pointer to call.</param>
  242. <param name="...">The function's parameters.</param>
  243. <returns>
  244. -
  245. </returns>
  246. <remarks>
  247. Call the function in the given pointer with the given parameters.
  248. </remarks>
  249. \**--------------------------------------------------------------------------**/
  250. stock CallStoredFunction(Function:func, GLOBAL_TAG_TYPES:...)
  251. {
  252. #pragma unused func
  253. new
  254. base,
  255. ctx[AsmContext];
  256. // Get this function.
  257. #emit CONST.pri CallStoredFunction
  258. #emit LOAD.alt AMX_HEADER_COD
  259. #emit ADD
  260. #emit STOR.S.pri base
  261. AsmInitPtr(ctx, base, 200);
  262. // Write safer code (to no longer crash).
  263. @emit PROC
  264. @emit LOAD.S.pri 12
  265. @emit JZER.rel 8
  266. @emit SCTRL 6
  267. @emit RETN
  268. // Recurse.
  269. #emit LCTRL 5
  270. #emit SCTRL 4
  271. #emit CONST.pri CallStoredFunction
  272. #emit ADD.C 4
  273. #emit SCTRL 6
  274. return 0;
  275. }
  276. /**--------------------------------------------------------------------------**\
  277. <summary>Callback_Get</summary>
  278. <param name="callback:name">Callback to find by name.</param>
  279. <param name="ret[E_CALLBACK_DATA]">Where to store the pointer.</param>
  280. <param name="expect[]">What parameters the function takes.</param>
  281. <param name="remote">Is this function called on one or all scripts?</param>
  282. <returns>
  283. -
  284. </returns>
  285. <remarks>
  286. Looks up the callback by name. If the name has the correct data embedded
  287. within it that's great and we use that directly. Otherwise this function
  288. loops backwards over the callbacks currently in scope (mostly) to the start
  289. of the parent function. If a match is still not found this looks for a
  290. public function of the same name. If that isn't found either it gives up.
  291. The new "remote" parameter returns instantly with a remote public function
  292. stub, and no stored data.
  293. </remarks>
  294. \**--------------------------------------------------------------------------**/
  295. stock bool:Callback_Get(callback:name, ret[E_CALLBACK_DATA], expect[] = "", bool:remote = false)
  296. {
  297. P:2("Callback_Get called: %s", name);
  298. Inline_Reset(ret);
  299. if (!(callback_tag:0 < name[0] < callback_tag:128))
  300. {
  301. P:3("Callback_Get: Found resolved callback.");
  302. // Resolved inline.
  303. return bool:memcpy(_:ret[E_CALLBACK_DATA:0], name[0], 0, _:E_CALLBACK_DATA * 4, _:E_CALLBACK_DATA);
  304. }
  305. else if (name[0] == callback_tag:'\03')
  306. {
  307. // This prefix is ALWAYS for publics not inlines.
  308. return bool:(ret[E_CALLBACK_DATA_POINTER] = (remote ? GetRemoteFunction(name[1], expect) : GetLocalFunction(name[1], expect)));
  309. }
  310. else if (remote)
  311. {
  312. // "remote" functions must always be done this way.
  313. return bool:(ret[E_CALLBACK_DATA_POINTER] = GetRemoteFunction(name, expect));
  314. }
  315. new
  316. pos = strfind(name, "\02"),
  317. frm = GetCurrentFramePreviousFrame(),
  318. prf = GetFramePreviousFrame(frm);
  319. if (pos == -1)
  320. {
  321. new
  322. cur = YSI_g_sPrevInlineFunc,
  323. parent = GetFrameFunction(prf),
  324. res;
  325. pos = strlen(name),
  326. frm = GetFrameReturn(frm);
  327. // Find the function by name.
  328. while (cur)
  329. {
  330. #emit PUSH.S pos
  331. #emit PUSH.C 0
  332. #emit PUSH.S name
  333. #emit PUSH.S cur
  334. #emit PUSH.C 16
  335. #emit SYSREQ.C strcmp
  336. #emit STACK 20
  337. #emit STOR.S.pri res
  338. if (res == 0 && AMX_Read(cur + pos * 4) == '\02')
  339. {
  340. if (parent <= AMX_Read(cur + pos * 4 + 1 * 4) <= frm)
  341. {
  342. // Rewrite the value of "name" for the subsequent code.
  343. #emit LOAD.S.pri cur
  344. #emit STOR.S.pri name
  345. break;
  346. }
  347. }
  348. static const
  349. gsSearch[] = "\02";
  350. #emit PUSH.C 0
  351. #emit PUSH.C 0
  352. #emit PUSH.C gsSearch
  353. #emit PUSH.S cur
  354. #emit PUSH.C 16
  355. #emit SYSREQ.C strfind
  356. #emit STACK 20
  357. #emit LOAD.S.alt cur
  358. #emit IDXADDR
  359. #emit ADD.C 16
  360. #emit LOAD.I
  361. #emit STOR.S.pri cur
  362. }
  363. if (!cur)
  364. {
  365. // Try publics...
  366. return bool:(ret[E_CALLBACK_DATA_POINTER] = GetLocalFunction(name, expect));
  367. }
  368. }
  369. static
  370. spec[2];
  371. Inline_EncodeFormatString(expect, spec),
  372. ret[E_CALLBACK_DATA_FORMAT] = spec,
  373. // Load this inline function's data in to our closure (and allocate memory).
  374. ret[E_CALLBACK_DATA_POINTER] = Function:name[pos + 1];
  375. new
  376. to = name[pos + 2],
  377. local = to >> 8, // Sign-extending!
  378. params = to & 0xFF,
  379. stack = params + local + 3,
  380. Alloc:a = malloc(stack); // Allocate closure space.
  381. // printf("local: %d, params: %d, stack: %d", local, params, stack);
  382. if (a)
  383. {
  384. // Get the address of the data.
  385. #emit CONST.alt YSI_gMallocMemory
  386. #emit LOAD.S.pri a
  387. #emit IDXADDR
  388. #emit STOR.S.pri frm
  389. // Get the stack size in bytes.
  390. stack <<= 2,
  391. ret[E_CALLBACK_DATA_ALLOC] = ResolvedAlloc:frm,
  392. ret[E_CALLBACK_DATA_OFFSET] = stack,
  393. // Copy the stack over. First get the frame of the function that used
  394. // an inline function, thus calling us indirectly.
  395. prf -= local << 2,
  396. // Adjust to the bottom of that stack, at least the bottom of the parts
  397. // we need (there may be more locals declared later that we don't need).
  398. // Copy "stack" bytes from "prf" to "frm".
  399. rawMemcpy(frm, prf, stack),
  400. // Save the "return" address for the inline to our fake stack.
  401. mset(a, local + 1, name[pos + 3]),
  402. // Save the parameter count (may be mangled by y_hooks).
  403. mset(a, local + 2, params << 2);
  404. P:2("Callback_Get end");
  405. return true;
  406. }
  407. return false;
  408. }
  409. /**--------------------------------------------------------------------------**\
  410. <summary>Callback_Release</summary>
  411. <param name="input[E_CALLBACK_DATA]">Callback to release.</param>
  412. <returns>
  413. -
  414. </returns>
  415. <remarks>
  416. Releases all the data associated with a given callback (closure storage).
  417. </remarks>
  418. \**--------------------------------------------------------------------------**/
  419. stock Callback_Release(input[E_CALLBACK_DATA])
  420. {
  421. // Check we were called by the correct frame.
  422. static
  423. ResolvedAlloc:ra,
  424. Alloc:a;
  425. if ((ra = input[E_CALLBACK_DATA_ALLOC]))
  426. {
  427. #emit CONST.alt YSI_gMallocMemory
  428. #emit LOAD.pri ra
  429. #emit SUB
  430. #emit SHR.C.pri 2
  431. #emit STOR.pri a
  432. // Publics don't have any stored data.
  433. free(a),
  434. input[E_CALLBACK_DATA_ALLOC] = ResolvedAlloc:0;
  435. }
  436. return _:(input[E_CALLBACK_DATA_POINTER] = Function:0);
  437. }
  438. /**--------------------------------------------------------------------------**\
  439. <summary>Callback_Restore</summary>
  440. <param name="func[E_CALLBACK_DATA]">Info on the restoration function.</param>
  441. <returns>
  442. -
  443. </returns>
  444. <remarks>
  445. Makes variables referenced, instead of valued. When used after
  446. "Callback_Call" the values of any variables in the enclosing function that
  447. were modified in the inline function will be propgated so that their new
  448. values are seen by the original parent function (rather than that function
  449. still seeing the original values prior to the inline function modifying
  450. them). Note that this does no checks at all at the minute - if you call an
  451. inline function whose parent is not currently on the stack, this will
  452. probably fail catastrophically!
  453. </remarks>
  454. \**--------------------------------------------------------------------------**/
  455. stock Callback_Restore(func[E_CALLBACK_DATA])
  456. {
  457. if (func[E_CALLBACK_DATA_ALLOC])
  458. {
  459. // Copy the closure data back over the calling function.
  460. new
  461. frm = GetFramePreviousFrame(GetCurrentFramePreviousFrame()),
  462. ret = GetFrameReturn(frm),
  463. pfr = GetFramePreviousFrame(frm);
  464. rawMemcpy(frm + 12 + GetFrameParameterSize(frm) - func[E_CALLBACK_DATA_OFFSET], _:func[E_CALLBACK_DATA_ALLOC], func[E_CALLBACK_DATA_OFFSET]),
  465. SetFrameReturn(frm, ret),
  466. SetFramePreviousFrame(frm, pfr);
  467. }
  468. return 0;
  469. }
  470. /*
  471. ,ad8888ba, 88 88 88
  472. d8"' `"8b 88 88 ""
  473. d8' 88 88
  474. 88 ,adPPYYba, 88 88 88 8b,dPPYba, ,adPPYb,d8
  475. 88 "" `Y8 88 88 88 88P' `"8a a8" `Y88
  476. Y8, ,adPPPPP88 88 88 88 88 88 8b 88
  477. Y8a. .a8P 88, ,88 88 88 88 88 88 "8a, ,d88
  478. `"Y8888Y"' `"8bbdP"Y8 88 88 88 88 88 `"YbbdP"Y8
  479. aa, ,88
  480. "Y8bbdP"
  481. */
  482. /**--------------------------------------------------------------------------**\
  483. <summary>Callback_Call</summary>
  484. <param name="func[E_CALLBACK_DATA]">Info on the function to be called.</param>
  485. <returns>
  486. -
  487. </returns>
  488. <remarks>
  489. Takes an inline function handler and parameters, and either calls the
  490. public function while passing through the parameters, or just jumps to the
  491. carefully crafted inline function code.
  492. </remarks>
  493. \**--------------------------------------------------------------------------**/
  494. stock Callback_Call(const func[E_CALLBACK_DATA], GLOBAL_TAG_TYPES:...)
  495. {
  496. #pragma unused func
  497. new
  498. base,
  499. ctx[AsmContext];
  500. // Get this function.
  501. #emit CONST.pri Callback_Call
  502. #emit LOAD.alt AMX_HEADER_COD
  503. #emit ADD
  504. #emit STOR.S.pri base
  505. AsmInitPtr(ctx, base, 200);
  506. // Write safer code (to no longer crash).
  507. @emit PROC
  508. @emit LOAD.S.pri 12
  509. @emit ADD.C 4
  510. @emit LOAD.I
  511. @emit JZER.rel 8
  512. @emit SCTRL 6
  513. @emit RETN
  514. // Recurse.
  515. #emit LCTRL 5
  516. #emit SCTRL 4
  517. #emit CONST.pri Callback_Call
  518. #emit ADD.C 4
  519. #emit SCTRL 6
  520. return 0;
  521. }
  522. /**--------------------------------------------------------------------------**\
  523. <summary>Callback_Array</summary>
  524. <param name="func[E_CALLBACK_DATA]">Info on the function to be called.</param>
  525. <param name="params[]">Array of data pointers.</param>
  526. <param name="num">Size of the array.</param>
  527. <returns>
  528. -
  529. </returns>
  530. <remarks>
  531. This is very similar to Callback_Call, but takes an array of ADDRESSES
  532. instead of normal parameters. This is designed to help support some
  533. experimental OO code I was working on...
  534. If the target is a public function, the parameters are resolved and passed
  535. normally. If the target is an inline function we are optimised for the
  536. common case, so move the data on to the stack (currently done value-by-value
  537. not all at once) and call "Callback_Call".
  538. The new assembly is based on "rawMemset" in "y_utils".
  539. </remarks>
  540. \**--------------------------------------------------------------------------**/
  541. stock Callback_Array(const func[E_CALLBACK_DATA], const params[], num = sizeof (params))
  542. {
  543. new
  544. base,
  545. ctx[AsmContext];
  546. // Get this function.
  547. #emit CONST.pri Callback_Array
  548. #emit LOAD.alt AMX_HEADER_COD
  549. #emit ADD
  550. #emit STOR.S.pri base
  551. AsmInitPtr(ctx, base, 200); // Don't need any more than that.
  552. // Start re-writing the function. First copy "params" on to the stack.
  553. @emit PROC
  554. @emit LOAD.S.pri 20
  555. @emit JZER.rel (26 * 4) // "num" is zero, do the simpler version.
  556. // @emit SHL.C.pri 2
  557. @emit SMUL.C (-4)
  558. @emit STOR.pri (base + 17 * 4)
  559. @emit NEG
  560. @emit STOR.pri (base + 23 * 4)
  561. @emit ADD.C 4
  562. @emit STOR.pri (base + 27 * 4)
  563. // Adjust the stack by "-num * 4" bytes.
  564. @emit STACK 0 // Value dynamically rewritten above.
  565. // Store the new pointer in "alt".
  566. @emit STACK 0 // Equivalent to: LCTRL 4; MOVE.alt (not rewritten).
  567. // Copy "num * 4" bytes from *pri to *alt (i.e. on to the stack).
  568. @emit LOAD.S.pri 16
  569. @emit MOVS 0 // Value dynamically rewritten above.
  570. // Call the next function.
  571. @emit PUSH.S 12
  572. @emit PUSH.C 0
  573. #emit CONST.pri Callback_Call
  574. #emit STOR.S.pri base
  575. @emit CALL (base + AMX_REAL_DATA + AMX_HEADER_COD)
  576. // End.
  577. @emit RETN
  578. // No parameters. Call the inline function directly.
  579. @emit LOAD.S.pri 12
  580. @emit ADD.C 4
  581. @emit LOAD.I
  582. @emit SCTRL 6
  583. // Only here to appease the decompiler (sometimes).
  584. @emit NOP
  585. // Covertly call the newly re-written version of this function.
  586. #emit LCTRL 5
  587. #emit SCTRL 4
  588. #emit CONST.pri Callback_Array
  589. #emit ADD.C 4
  590. #emit SCTRL 6
  591. // Never called, but includes the other function.
  592. return Callback_Call(func, params, num);
  593. }
  594. /**--------------------------------------------------------------------------**\
  595. <summary>Inline_Timer</summary>
  596. <param name="func[]">The function to call on a delay.</param>
  597. <param name="delay">how long before the first call?</param>
  598. <param name="interval">How long between subsequent calls?</param>
  599. <param name="repeat">How many times to call the function.</param>
  600. <param name="format[]">The additional parameters' types.</param>
  601. <param name="...">The additional parameters.</param>
  602. <returns>
  603. -
  604. </returns>
  605. <remarks>
  606. Calls a function, which may be an inline function, after a given delay, and
  607. with the given regularity after that. The parameters are slightly different
  608. to those in SetTimer - that takes only an interval and a repeat boolean.
  609. This instead takes two times - the first is the delay before the first call,
  610. the second is the delay between all subsequent calls (mainly to offset
  611. different timers within a given period). The "repeat" parameter is also
  612. different - instead of being a boolean, it is a count. "0" no longer means
  613. "don't repeat", but "repeat forever". "1" no longer means "repeat forever",
  614. but "call once". All other numbers (beside 0) specify an exact number of
  615. times to call the function before calling it no more. This is in line with
  616. the "SetTimer_" and "SetTimerEx_" functions in the fixes2 plugin.
  617. </remarks>
  618. \**--------------------------------------------------------------------------**/
  619. /*
  620. db ad88888ba 88b d88 ad88888ba
  621. d88b d8" "8b 888b d888 d8" "8b ,d ,d
  622. d8'`8b Y8, 88`8b d8'88 Y8, 88 88
  623. d8' `8b `Y8aaaaa, 88 `8b d8' 88 `Y8aaaaa, MM88MMM ,adPPYYba, 8b,dPPYba, MM88MMM
  624. d8YaaaaY8b `"""""8b, 88 `8b d8' 88 `"""""8b, 88 "" `Y8 88P' "Y8 88
  625. d8""""""""8b `8b 88 `8b d8' 88 `8b 88 ,adPPPPP88 88 88
  626. d8' `8b Y8a a8P 88 `888' 88 Y8a a8P 88, 88, ,88 88 88,
  627. d8' `8b "Y88888P" 88 `8' 88 "Y88888P" "Y888 `"8bbdP"Y8 88 "Y888
  628. */
  629. /**--------------------------------------------------------------------------**\
  630. <summary>I@E</summary>
  631. <param name="s">The array to store an inline function's data in.</param>
  632. <param name="constFunc">Should this function copy the stack back?</param>
  633. <returns>
  634. -
  635. </returns>
  636. <remarks>
  637. AKA. Inline_Entry
  638. This function gets the start of an inline function's code block. It then
  639. removes itself from the compiled code so that it can never be called agian.
  640. If "constFunc" is 3, copy the stack back, if it isn't don't.
  641. </remarks>
  642. \**--------------------------------------------------------------------------**/
  643. stock I@E(/* mutable */ const s[])
  644. {
  645. P:2("Inline_Entry called: %s", s);
  646. new
  647. start = GetCurrentFrameReturn() - 24,
  648. ctx[DisasmContext];
  649. DisasmInit(ctx, start, start + 32);
  650. // Get parameter.
  651. // There is a chance that "s" has the same value as an opcode. However, if
  652. // that is the case it will EITHER have the same value as "PUSH.C" OR the
  653. // same value as "PUSH.pri" - it can't have the same value as both, so this
  654. // code will still catch that case.
  655. if (!DisasmDecodeInsn(ctx) || DisasmGetOpcode(ctx) != OP_PUSH_C || DisasmGetOperand(ctx) != GetCurrentFrameParameter(0))
  656. {
  657. // Compiled with extra debug information.
  658. start -= 4,
  659. ctx[DisasmContext_nip] = ctx[DisasmContext_start_ip] -= 4,
  660. // This mode uses "CONST.pri x; PUSH.pri" instead of "PUSH.C x".
  661. DisasmDecodeInsn(ctx);
  662. if (DisasmGetOpcode(ctx) != OP_CONST_PRI || DisasmGetOperand(ctx) != GetCurrentFrameParameter(0)) return Debug_Print0(YSI_g_scError);
  663. DisasmDecodeInsn(ctx);
  664. if (DisasmGetOpcode(ctx) != OP_PUSH_PRI) return Debug_Print0(YSI_g_scError);
  665. }
  666. // Function parameter count.
  667. DisasmDecodeInsn(ctx);
  668. if (DisasmGetOpcode(ctx) != OP_PUSH_C || DisasmGetOperand(ctx) != 4) return Debug_Print0(YSI_g_scError);
  669. // Function call.
  670. DisasmDecodeInsn(ctx);
  671. if (DisasmGetOpcode(ctx) != OP_CALL) return Debug_Print0(YSI_g_scError);
  672. // Jump.
  673. DisasmDecodeInsn(ctx);
  674. if (DisasmGetOpcode(ctx) != OP_JZER) return Debug_Print0(YSI_g_scError);
  675. // Write out the jump for future calls.
  676. new
  677. addr1 = AMX_Read(YSI_g_sPrevJumpOver),
  678. addr2 = start + AMX_HEADER_COD,
  679. frm = DisasmGetOperand(ctx);
  680. if (YSI_g_sPrevJumpOver && addr1 == addr2 + AMX_REAL_ADDRESS - AMX_BASE_ADDRESS)
  681. {
  682. // Multiple inline functions in a row. Jump over them all.
  683. AMX_Write(YSI_g_sPrevJumpOver, frm),
  684. YSI_g_sCurInlineEntry = start;
  685. }
  686. else if (YSI_g_sPrevJumpOver && addr1 == addr2 + AMX_REAL_ADDRESS - AMX_BASE_ADDRESS - 4 && Opcode:AMX_Read(addr2) == RelocateOpcode(OP_BREAK))
  687. {
  688. // Multiple inline functions in a row. Jump over them all.
  689. AMX_Write(YSI_g_sPrevJumpOver, frm),
  690. YSI_g_sCurInlineEntry = start - 4;
  691. }
  692. else
  693. {
  694. new
  695. ctx2[AsmContext];
  696. AsmInitPtr(ctx2, addr2, 8),
  697. AsmEmitJump(ctx2, frm),
  698. YSI_g_sPrevJumpOver = addr2 + 4,
  699. YSI_g_sCurInlineEntry = start + 8;
  700. }
  701. // Store the pointer to the start of this new inline's available code.
  702. // =========================================================================
  703. // Save the data name pointer.
  704. addr2 = ref(s[strfind(s, ":")]),
  705. AMX_Write(addr2, '\02;'),
  706. AMX_Write(addr2 + 4, YSI_g_sCurInlineEntry),
  707. // Get the number of local variables already in the parent function.
  708. frm = GetCurrentFramePreviousFrame(),
  709. YSI_g_sCurInlineLocals = GetFrameLocalSize(frm),
  710. YSI_g_sCurInlineParams = GetFrameParameterSize(frm),
  711. // y_hooks mangles the parameter count. In this case, the correct parameter
  712. // count is stored in the previous frame, which is the entry point for the
  713. // generated hook function stub.
  714. YSI_g_sCurInlineParams = (YSI_g_sCurInlineParams == -4) ? GetFrameParameterSize(GetFramePreviousFrame(frm)) : YSI_g_sCurInlineParams;
  715. P:C(if (YSI_g_sCurInlineParams < 0) P:E("Inline_Entry: Invalid parameter size."););
  716. // Save parameter counts, shifted for separate components.
  717. AMX_Write(addr2 + 8, (YSI_g_sCurInlineLocals ? (YSI_g_sCurInlineLocals << 6) : (-4 << 6)) | (YSI_g_sCurInlineParams >> 2)),
  718. // =========================================================================
  719. // Build a linked list of inlines that are in scope.
  720. AMX_Write(addr2 + 16, YSI_g_sPrevInlineFunc),
  721. YSI_g_sCurInlinePointer = addr2 + 12,
  722. YSI_g_sPrevInlineFunc = ref(s);
  723. // Return 1 to enter the main "inline" function block.
  724. return 1;
  725. }
  726. /*
  727. db ad88888ba 88b d88 8b d8
  728. d88b d8" "8b 888b d888 `8b d8'
  729. d8'`8b Y8, 88`8b d8'88 `8b d8'
  730. d8' `8b `Y8aaaaa, 88 `8b d8' 88 `8b d8' ,adPPYYba, 8b,dPPYba, ,adPPYba,
  731. d8YaaaaY8b `"""""8b, 88 `8b d8' 88 `8b d8' "" `Y8 88P' "Y8 I8[ ""
  732. d8""""""""8b `8b 88 `8b d8' 88 `8b d8' ,adPPPPP88 88 `"Y8ba,
  733. d8' `8b Y8a a8P 88 `888' 88 `888' 88, ,88 88 aa ]8I
  734. d8' `8b "Y88888P" 88 `8' 88 `8' `"8bbdP"Y8 88 `"YbbdP"'
  735. */
  736. /**--------------------------------------------------------------------------**\
  737. <summary>I@F</summary>
  738. <returns>
  739. -
  740. </returns>
  741. <remarks>
  742. AKA. Inline_Allocator.
  743. This function determines the exact address of the start of the main inline
  744. function container loop. That is, the label that things like "continue"
  745. jump to so that we know how much space we have to play with and where it is.
  746. </remarks>
  747. \**--------------------------------------------------------------------------**/
  748. stock I@F()
  749. {
  750. P:2("Inline_Allocator called");
  751. // This function takes up almost no space in a .pwn, but loads in a .amx.
  752. // Or at least as much as I want it to in order to give me code space to
  753. // play with.
  754. new
  755. start = GetCurrentFrameReturn(),
  756. ctx[DisasmContext];
  757. // "end" isn't currently used in "disasm", but we can't guarantee that.
  758. DisasmInit(ctx, start, start + 16);
  759. DisasmDecodeInsn(ctx);
  760. if (DisasmGetOpcode(ctx) != OP_JZER) return Debug_Print0(YSI_g_scError);
  761. // Get the end of the outer loop.
  762. YSI_g_sCurInlineCode = DisasmGetOperand(ctx);
  763. if (DisasmDecodeInsn(ctx) && DisasmGetOpcode(ctx) == OP_BREAK)
  764. {
  765. if (DisasmDecodeInsn(ctx) && DisasmGetOpcode(ctx) == OP_BREAK)
  766. {
  767. // Two breaks in a row - skip one.
  768. start += 4;
  769. }
  770. else Debug_Print0(YSI_g_scError);
  771. }
  772. // "start" now (hopefully) points to the main loop start address.
  773. YSI_g_sCurInlineLoop = start + 8;
  774. return 1;
  775. }
  776. /*
  777. db ad88888ba 88b d88 88888888888 88
  778. d88b d8" "8b 888b d888 88 88
  779. d8'`8b Y8, 88`8b d8'88 88 88
  780. d8' `8b `Y8aaaaa, 88 `8b d8' 88 88aaaaa 8b,dPPYba, ,adPPYb,88
  781. d8YaaaaY8b `"""""8b, 88 `8b d8' 88 88""""" 88P' `"8a a8" `Y88
  782. d8""""""""8b `8b 88 `8b d8' 88 88 88 88 8b 88
  783. d8' `8b Y8a a8P 88 `888' 88 88 88 88 "8a, ,d88
  784. d8' `8b "Y88888P" 88 `8' 88 88888888888 88 88 `"8bbdP"Y8
  785. */
  786. /**--------------------------------------------------------------------------**\
  787. <summary>I@L</summary>
  788. <returns>
  789. 0
  790. </returns>
  791. <remarks>
  792. AKA. Inline_Main.
  793. The code before the start of the function is split in to three parts:
  794. The first part comes before the start of the loop condition, and is where
  795. all the variables are initialised in the compiled code. As we don't want to
  796. initialise any variables, this can be repurposed for function entry code.
  797. The address of this is stored in "entry", and it ends at "loop".
  798. The second part is where the function loops back to. This MUST start with a
  799. "RETN" instruction to end the function in all cases, so any startup code in
  800. the first segment must jump over that "RETN". The remainder of this section
  801. can be used for any more entry or exit code that is required. Note that
  802. it can also start with a "STACK" opcode when required. This section starts
  803. at "loop" and ends at "code".
  804. The final segment is not technically BEFORE the main function code but
  805. AFTER. That's normally where the stack is restored, but we now have full
  806. control of that (so don't forget to write it in to the process exit code).
  807. "Inline_Allocator" currently marks the end of the first segment, and
  808. "Inline_Main" marks the end of the second segment.
  809. </remarks>
  810. \**--------------------------------------------------------------------------**/
  811. static stock
  812. YSI_g_sJumpAddress,
  813. YSI_g_sRequiredSpace;
  814. forward Inline_OnAsmError(ctx[AsmContext], AsmError:error);
  815. public Inline_OnAsmError(ctx[AsmContext], AsmError:error)
  816. {
  817. if (numargs() == 1) error = AsmGetError(ctx);
  818. // Actually USE the error to write the "JUMP" instruction correctly.
  819. switch (error)
  820. {
  821. case ASM_ERROR_SPACE:
  822. {
  823. if (YSI_g_sJumpAddress) P:E("ASM_ERROR_SPACE in Inline_Main.");
  824. else
  825. {
  826. // Get where the last instruction started to be written.
  827. ctx[AsmContext_buffer_offset] = YSI_g_sJumpAddress = AsmGetPreviousWriteOffset(),
  828. // Calculate how much of the function header was NOT written.
  829. YSI_g_sRequiredSpace -= YSI_g_sJumpAddress,
  830. // Save the address of the "JUMP" operand for later.
  831. YSI_g_sJumpAddress += 4 + ctx[AsmContext_buffer],
  832. // Allocate space for writing the "JUMP" (previously skipped).
  833. ctx[AsmContext_buffer_size] = cellmax;
  834. // Jump over the function end.
  835. @emit JUMP 0
  836. new
  837. target = YSI_g_sCurInlineCode + AMX_HEADER_COD - ctx[AsmContext_buffer] - YSI_g_sRequiredSpace;
  838. // Pad everything with "NOP"s.
  839. while (ctx[AsmContext_buffer_offset] != target) @emit NOP
  840. // Write the jump target as here.
  841. AMX_Write(YSI_g_sJumpAddress, ctx[AsmContext_buffer] + ctx[AsmContext_buffer_offset] - AMX_BASE_ADDRESS + AMX_REAL_ADDRESS);
  842. }
  843. }
  844. case ASM_ERROR_OPCODE : P:E("ASM_ERROR_OPCODE in Inline_Main.");
  845. case ASM_ERROR_OPERAND: P:E("ASM_ERROR_OPERAND in Inline_Main.");
  846. }
  847. }
  848. // "tryemit"
  849. // This macro detects when an opcode couldn't be written due to a lack of space
  850. // in the buffer, then instantly re-writes the same opcode to the same context!
  851. // The reason this works is that there is an error handler that is called BEFORE
  852. // "amx_emit_" returns the error code, and that handler deals with all the
  853. // complex reallocations and jumps required to move the code about (in this case
  854. // to fit around the existing jumps in the code around where we are writing new
  855. // code).
  856. #define _@emit%0\32;%1\10;%3 if(asm_emit_(ctx,%1 ) == ASM_ERROR_SPACE) asm_emit_(ctx,%1 );
  857. stock I@K(...)
  858. {
  859. return numargs() * 3;
  860. }
  861. /*
  862. ,ad8888ba, 88
  863. d8"' `"8b 88
  864. d8' 88
  865. 88 ,adPPYba, ,adPPYb,88 ,adPPYba, ,adPPYb,d8 ,adPPYba, 8b,dPPYba,
  866. 88 a8" "8a a8" `Y88 a8P_____88 a8" `Y88 a8P_____88 88P' `"8a
  867. Y8, 8b d8 8b 88 8PP""""""" 8b 88 8PP""""""" 88 88
  868. Y8a. .a8P "8a, ,a8" "8a, ,d88 "8b, ,aa "8a, ,d88 "8b, ,aa 88 88
  869. `"Y8888Y"' `"YbbdP"' `"8bbdP"Y8 `"Ybbd8"' `"YbbdP"Y8 `"Ybbd8"' 88 88
  870. aa, ,88
  871. "Y8bbdP"
  872. */
  873. stock I@L(constFunc, ...)
  874. {
  875. //new
  876. // bool:constFunc = I@;
  877. P:2("Inline_Main called (%d)", numargs());
  878. // MOST IMPORTANT THING TO DO FIRST! Get the address to jump back to.
  879. new
  880. ret = GetCurrentFrameReturn(),
  881. heapClean = 0,
  882. dctx[DisasmContext];
  883. DisasmInit(dctx, ret, ret + 20),
  884. // Get the next code instruction.
  885. DisasmDecodeInsn(dctx);
  886. // There is some heap to clean up, how much?
  887. if (DisasmGetOpcode(dctx) == OP_HEAP)
  888. {
  889. ret += 8,
  890. heapClean = DisasmGetOperand(dctx),
  891. DisasmDecodeInsn(dctx);
  892. }
  893. // Get the jump to the end of the main loop.
  894. if (DisasmGetOpcode(dctx) != OP_JZER) return Debug_Print0(YSI_g_scError);
  895. // Set this function to return to the end of the outer loop.
  896. SetCurrentFrameReturn(YSI_g_sCurInlineCode - AMX_REAL_ADDRESS - (AMX_HEADER_COD - AMX_BASE_ADDRESS));
  897. // =========================================================================
  898. // NOW WE CAN DO THE MAIN FUNCTION CODE.
  899. // =========================================================================
  900. new
  901. argSize = GetFrameLocalSize(GetCurrentFramePreviousFrame());
  902. // Do we have enough space for everything.
  903. // Get the start of the user's function code. The real start of THEIR code.
  904. ret += 8;
  905. if (DisasmDecodeInsn(dctx) && DisasmGetOpcode(dctx) == OP_BREAK) ret += 4;
  906. YSI_g_sCurInlineCode = ret;
  907. P:5("Inline_Main: YSI_g_sCurInlineEntry = %x", YSI_g_sCurInlineEntry);
  908. P:5("Inline_Main: YSI_g_sCurInlineLoop = %x", YSI_g_sCurInlineLoop);
  909. P:5("Inline_Main: YSI_g_sCurInlineCode = %x", YSI_g_sCurInlineCode);
  910. // Get the segment sizes and variable type counts.
  911. new
  912. args = numargs() - 1,
  913. varCount = 0;
  914. #define arrCount (args - varCount - strCount)
  915. #define strCount ret
  916. strCount = 0;
  917. for (new i = 1; i <= args; ++i)
  918. {
  919. switch (getarg(i))
  920. {
  921. case 0, -1: ++varCount;
  922. case cellmax: ++strCount;
  923. }
  924. }
  925. P:5("Inline_Main: varCount = %d", varCount);
  926. P:5("Inline_Main: arrCount = %d", arrCount);
  927. P:5("Inline_Main: strCount = %d", strCount);
  928. // So-far, so-good. The code above in preliminary tests seems to correctly
  929. // identify all the relevant and important points in the user code.
  930. new
  931. ctx[AsmContext];
  932. // =========================================================================
  933. // CODE SPACE AVAILABLE
  934. // =========================================================================
  935. //
  936. // Inline_Entry: 6
  937. // Vars: 2 v+r
  938. // Arrays: 7 a
  939. // JUMP: 2
  940. // Inline_Allocator: 6
  941. //
  942. // Arrays: 6 a
  943. // Vars: 5 v
  944. // Refs: 6 r
  945. //
  946. // Inline_Main: 19
  947. //
  948. // Simple Total: 14 + 7v + 8r + 19
  949. // Complex Total: 14 + 7v + 8r + 17a + 19
  950. //
  951. // Simple Required: 15 + 3v + 7r + 16
  952. // Complex Required: 15 + 4v + 8r + 6a + 16
  953. //
  954. // =========================================================================
  955. // SEGMENT 1 - RESOLVE AND PUSH CLOSURE DATA AND PARAMETERS
  956. // =========================================================================
  957. YSI_g_sJumpAddress = 0,
  958. AsmInitPtr(ctx, YSI_g_sCurInlineEntry + AMX_HEADER_COD, YSI_g_sCurInlineLoop - YSI_g_sCurInlineEntry - 8),
  959. AsmSetErrorHandler(ctx, GetPublicAddressFromName("Inline_OnAsmError"));
  960. // Space for params + count.
  961. new
  962. // Size of stored closure data.
  963. offset = (YSI_g_sCurInlineParams + YSI_g_sCurInlineLocals + 12);
  964. if (varCount == args) // Only variables.
  965. {
  966. // =====================================================================
  967. // SIMPLE CODE - NO ARRAYS OR STRINGS
  968. // =====================================================================
  969. if (YSI_g_sCurInlineLocals)
  970. {
  971. P:5("Inline_Main: TYPE 0");
  972. YSI_g_sRequiredSpace = 4 * 3 * args + 4 * 13;
  973. _@emit STACK (-offset)
  974. _@emit ADDR.alt (-offset) // Like "LCTRL 4; XCHG", but shorter.
  975. _@emit LREF.S.pri 12 // Load "func" variable.
  976. // Sadly "MOVS" checks that the destination is valid and in the stack, which
  977. // is what we didn't want.
  978. _@emit MOVS offset
  979. // Reduce the stack again. This is the shortest way I know (5 cells).
  980. _@emit STACK (4 + YSI_g_sCurInlineLocals)
  981. _@emit PROC // Set up the new frame.
  982. _@emit STACK (-YSI_g_sCurInlineLocals) // Skip other locals again.
  983. new
  984. // Get the offset to the calling function's passed parameters.
  985. // 12 for this function's header, plus this function's parameters, plus
  986. // the last function's locals (currently none, MUST BE KEPT UP TO DATE),
  987. // plus the last function's header, plus the "func" variable.
  988. load = 12 + YSI_g_sCurInlineParams + 0 + 12 + 4,
  989. i = 0;
  990. while (i++ < args)
  991. {
  992. // Loop over the arguments and push them.
  993. _@emit LREF.S.pri load
  994. _@emit PUSH.pri
  995. load += 4;
  996. }
  997. }
  998. else
  999. {
  1000. P:5("Inline_Main: TYPE 1 (%d, %d, %d, %d, %d)", offset, offset - 4, args, 4 * 3 * args + 4 * 9, 12 + YSI_g_sCurInlineParams + 0 + 12 + 4);
  1001. // This is the simplest of the four versions of the code. Here
  1002. // there are no arrays to copy over, nor are there any local
  1003. // variables from the closure.
  1004. offset -= 4,
  1005. YSI_g_sRequiredSpace = 4 * 3 * args + 4 * 9;
  1006. _@emit STACK (-offset)
  1007. _@emit ADDR.alt (-offset)
  1008. _@emit LREF.S.pri 12
  1009. _@emit MOVS offset
  1010. _@emit PROC
  1011. new
  1012. load = 12 + YSI_g_sCurInlineParams + 0 + 12 + 4,
  1013. i = 0;
  1014. while (i++ < args)
  1015. {
  1016. _@emit LREF.S.pri load
  1017. _@emit PUSH.pri
  1018. load += 4;
  1019. }
  1020. offset += 4;
  1021. }
  1022. }
  1023. else
  1024. {
  1025. // =====================================================================
  1026. // COMPLEX CODE - ARRAYS OR STRINGS
  1027. // =====================================================================
  1028. if (YSI_g_sCurInlineLocals)
  1029. {
  1030. P:5("Inline_Main: TYPE 2");
  1031. YSI_g_sRequiredSpace = 4 * 4 * varCount + 4 * 6 * arrCount + 4 * 14 * strCount + 4 * 13;
  1032. _@emit STACK (-offset)
  1033. _@emit STACK (YSI_g_sCurInlineLocals - argSize)
  1034. _@emit LREF.S.pri 12
  1035. _@emit MOVS offset
  1036. new
  1037. load = 12 + 4,
  1038. stor = -offset,
  1039. i = 0;
  1040. while (i < args)
  1041. {
  1042. if ((varCount = getarg(++i)) == cellmax)
  1043. {
  1044. // String.
  1045. stor -= YSI_MAX_INLINE_STRING * 4;
  1046. _@emit PUSH.C YSI_MAX_INLINE_STRING
  1047. _@emit PUSH.S load // src = *cur
  1048. _@emit ZERO.S stor // dest[0] = '\0';
  1049. _@emit PUSH.adr stor // dst = &dest
  1050. _@emit PUSH.C 12
  1051. _@emit SYSREQ "strcat"
  1052. _@emit STACK 16
  1053. }
  1054. else
  1055. {
  1056. if ((varCount *= 4) <= 0)
  1057. {
  1058. // Normal variable.
  1059. stor -= 4;
  1060. _@emit LREF.S.pri load
  1061. _@emit STOR.S.pri stor
  1062. }
  1063. else
  1064. {
  1065. // Array.
  1066. stor -= varCount;
  1067. _@emit LOAD.S.pri load
  1068. _@emit ADDR.alt stor
  1069. _@emit MOVS varCount
  1070. }
  1071. }
  1072. load += 4;
  1073. }
  1074. _@emit STACK (4 + argSize)
  1075. _@emit PROC
  1076. _@emit STACK (-argSize)
  1077. }
  1078. else
  1079. {
  1080. P:5("Inline_Main: TYPE 3");
  1081. offset -= 4,
  1082. YSI_g_sRequiredSpace = 4 * 4 * varCount + 4 * 6 * arrCount + 4 * 14 * strCount + 4 * 11;
  1083. _@emit STACK (-offset)
  1084. _@emit ADDR.alt (-offset)
  1085. _@emit LREF.S.pri 12
  1086. _@emit MOVS offset
  1087. _@emit PROC
  1088. _@emit STACK (-argSize)
  1089. new
  1090. load = 12 + YSI_g_sCurInlineParams + 0 + 12 + 4,
  1091. stor = 0,
  1092. i = 0;
  1093. while (i < args)
  1094. {
  1095. if ((varCount = getarg(++i)) == cellmax)
  1096. {
  1097. // String.
  1098. stor -= YSI_MAX_INLINE_STRING * 4;
  1099. _@emit PUSH.C YSI_MAX_INLINE_STRING
  1100. _@emit PUSH.S load // src = *cur
  1101. _@emit ZERO.S stor // dest[0] = '\0';
  1102. _@emit PUSH.adr stor // dst = &dest
  1103. _@emit PUSH.C 12
  1104. _@emit SYSREQ "strcat"
  1105. _@emit STACK 16
  1106. }
  1107. else
  1108. {
  1109. if ((varCount *= 4) <= 0)
  1110. {
  1111. // Normal variable.
  1112. stor -= 4;
  1113. _@emit LREF.S.pri load
  1114. _@emit STOR.S.pri stor
  1115. }
  1116. else
  1117. {
  1118. // Array.
  1119. stor -= varCount;
  1120. _@emit LOAD.S.pri load
  1121. _@emit ADDR.alt stor
  1122. _@emit MOVS varCount
  1123. }
  1124. }
  1125. load += 4;
  1126. }
  1127. offset += 4;
  1128. }
  1129. }
  1130. // =========================================================================
  1131. // SEGMENT 2 - SAVE REFERENCES BACK
  1132. // =========================================================================
  1133. if (YSI_g_sJumpAddress == 0)
  1134. {
  1135. // Fake an error. This indirectly calls the "Inline_OnAsmError"
  1136. // function above to fill the intervening space with NOPs.
  1137. ctx[AsmContext_buffer_size] = 0;
  1138. @emit NOP
  1139. }
  1140. // The maths should now be correct.
  1141. AsmInitPtr(ctx, YSI_g_sCurInlineLoop + AMX_HEADER_COD, cellmax);
  1142. if (argSize)
  1143. {
  1144. @emit STACK argSize
  1145. YSI_g_sJumpAddress = YSI_g_sCurInlineLoop + 16;
  1146. }
  1147. else
  1148. {
  1149. YSI_g_sJumpAddress = YSI_g_sCurInlineLoop + 8;
  1150. }
  1151. @emit ZERO.pri // Default return value.
  1152. @emit RETN
  1153. // Save this return address, so the instruction above goes to the one below.
  1154. // Save references.
  1155. new
  1156. load = -offset,
  1157. stor = 12 + 4,
  1158. i = 0;
  1159. while (i < args)
  1160. {
  1161. varCount = getarg(++i);
  1162. switch (varCount)
  1163. {
  1164. case -1:
  1165. {
  1166. load -= 4;
  1167. @emit LOAD.S.alt load
  1168. @emit SREF.S.alt stor
  1169. }
  1170. case 0: load -= 4;
  1171. case cellmax: load -= YSI_MAX_INLINE_STRING * 4;
  1172. default: load -= varCount * 4;
  1173. }
  1174. stor += 4;
  1175. }
  1176. // Copy the closure back (if there is anything that needs copying).
  1177. if (constFunc && (YSI_g_sCurInlineParams || YSI_g_sCurInlineLocals))
  1178. {
  1179. if (!YSI_g_sCurInlineLocals) offset -= 4;
  1180. @emit STACK (-offset)
  1181. @emit PUSH.pri
  1182. @emit ADDR.pri (-offset)
  1183. @emit LREF.S.alt 12
  1184. @emit MOVS offset
  1185. @emit POP.pri
  1186. @emit STACK offset
  1187. }
  1188. // Finish.
  1189. @emit RETN
  1190. // =========================================================================
  1191. // STORE THE RETURN ADDRESS
  1192. // =========================================================================
  1193. AMX_Write(YSI_g_sCurInlinePointer, YSI_g_sJumpAddress),
  1194. AMX_Write(YSI_g_sCurInlinePointer - 12, '\02;');
  1195. // =========================================================================
  1196. // FUNCTION EPILOG
  1197. // =========================================================================
  1198. // Clean up the heap manually because we are jumping straight over the
  1199. // original cleanup code - in fact we may have destroyed it entirely by
  1200. // writing new code over it.
  1201. #emit LCTRL 2
  1202. #emit LOAD.S.alt heapClean
  1203. #emit ADD
  1204. #emit SCTRL 2
  1205. // Cleanup.
  1206. return 0;
  1207. #undef strCount
  1208. #undef arrCount
  1209. }
  1210. #undef _@emit
  1211. /*
  1212. 88888888ba 88 88 88
  1213. 88 "8b 88 88 ""
  1214. 88 ,8P 88 88
  1215. 88aaaaaa8P' 88 88 88,dPPYba, 88 88 ,adPPYba, ,adPPYba,
  1216. 88""""""' 88 88 88P' "8a 88 88 a8" "" I8[ ""
  1217. 88 88 88 88 d8 88 88 8b `"Y8ba,
  1218. 88 "8a, ,a88 88b, ,a8" 88 88 "8a, ,aa aa ]8I
  1219. 88 `"YbbdP'Y8 8Y"Ybbd8"' 88 88 `"Ybbd8"' `"YbbdP"'
  1220. */
  1221. /**--------------------------------------------------------------------------**\
  1222. <summary>StoredF_IsHooked</summary>
  1223. <param name="addr">Function start address.</param>
  1224. <returns>
  1225. Is the function at this address already hooked by us?
  1226. </returns>
  1227. <remarks>
  1228. -
  1229. </remarks>
  1230. \**--------------------------------------------------------------------------**/
  1231. static stock StoredF_IsHooked(addr)
  1232. {
  1233. // Find out if the given address points to a public function that we have
  1234. // already hooked in to for faster calling.
  1235. return AMX_Read(addr + AMX_HEADER_COD) == _:RelocateOpcode(OP_JUMP);
  1236. }
  1237. /**--------------------------------------------------------------------------**\
  1238. <summary>StoredF_OnPubGenError</summary>
  1239. <param name="ctx[AsmContext]">Current code generation context.</param>
  1240. <param name="AsmError:error">The error given.</param>
  1241. <returns>
  1242. -
  1243. </returns>
  1244. <remarks>
  1245. This is a fatal error as there isn't really anything we can do about it.
  1246. </remarks>
  1247. \**--------------------------------------------------------------------------**/
  1248. forward StoredF_OnPubGenError(ctx[AsmContext], AsmError:error);
  1249. public StoredF_OnPubGenError(ctx[AsmContext], AsmError:error)
  1250. {
  1251. // This is very bad, in fact I'd say this is a fatal error.
  1252. P:F("Inline_OnPubGenError called - Try increase\"YSI_Internal\\y_cgen.inc: _@_y_cgen_@_0\"'s code size");
  1253. }
  1254. /**--------------------------------------------------------------------------**\
  1255. <summary>StoredF_WritePublicCode</summary>
  1256. <param name="fptr">Function start address.</param>
  1257. <param name="spec[]">Function parameter types.</param>
  1258. <returns>
  1259. the new function pointer.
  1260. </returns>
  1261. <remarks>
  1262. Writes a stub for calling a public function with an alternate method.
  1263. Because "CallStoredFunction" (the call entry point) takes all its parameters
  1264. by reference and some of the actual function's parameters won't be, we have
  1265. to generate the code to convert those that aren't to values only. Also,
  1266. because "CallStoredFunction" takes an extra parameter that's the address of
  1267. the function to call, we have to wipe that from the stack and update the
  1268. resulting frame header.
  1269. </remarks>
  1270. \**--------------------------------------------------------------------------**/
  1271. static stock StoredF_WritePublicCode(fptr, const spec[])
  1272. {
  1273. new
  1274. entry = AMX_HEADER_PUBLICS + fptr * 8;
  1275. fptr = AMX_Read(entry);
  1276. if (StoredF_IsHooked(fptr)) return fptr + 8;
  1277. new
  1278. ctx[AsmContext];
  1279. CGen_UseCodeSpace(ctx),
  1280. AsmSetErrorHandler(ctx, GetPublicAddressFromName("StoredF_OnPubGenError"));
  1281. // Rewrite the function pointer.
  1282. new
  1283. nptr = CGen_GetCodeSpace() - AMX_HEADER_COD;
  1284. AMX_Write(entry, nptr);
  1285. // Get the absolute address of "fptr".
  1286. fptr += AMX_HEADER_COD + AMX_REAL_DATA;
  1287. // Jump to the original function when called in other ways.
  1288. @emit JUMP fptr
  1289. // Inline calls use the address AFTER that jump to do extra work.
  1290. // First, adjust the stack.
  1291. @emit POP.alt // Pop the previous frame.
  1292. @emit POP.pri // Pop the return address.
  1293. @emit SWAP.pri // Swap the return address and parameter count.
  1294. @emit ADD.C (-4) // Remove one parameter.
  1295. @emit STOR.S.pri 12 // Save the new parameter count.
  1296. // Update the frame pointer.
  1297. @emit PROC // Write a new frame pointer to update the stack.
  1298. @emit SWAP.alt // Swap the new "previous" frame with the real one.
  1299. // Reify reference variables that shouldn't be so.
  1300. new
  1301. var = strlen(spec);
  1302. entry = var * 4 + 8;
  1303. while (var--)
  1304. {
  1305. switch (spec[var])
  1306. {
  1307. case 'a', 's', 'v': {} // No modification required.
  1308. default:
  1309. {
  1310. // Resolve the reference's value.
  1311. @emit LREF.S.alt entry
  1312. @emit STOR.S.alt entry
  1313. }
  1314. }
  1315. entry -= 4;
  1316. }
  1317. // Now we jump to the original function.
  1318. @emit JUMP (fptr + 4)
  1319. CGen_AddCodeSpace(AsmGetCodeSize(ctx));
  1320. return nptr + 8;
  1321. }
  1322. /*
  1323. 88888888ba
  1324. 88 "8b ,d
  1325. 88 ,8P 88
  1326. 88aaaaaa8P' ,adPPYba, 88,dPYba,,adPYba, ,adPPYba, MM88MMM ,adPPYba, ,adPPYba,
  1327. 88""""88' a8P_____88 88P' "88" "8a a8" "8a 88 a8P_____88 I8[ ""
  1328. 88 `8b 8PP""""""" 88 88 88 8b d8 88 8PP""""""" `"Y8ba,
  1329. 88 `8b "8b, ,aa 88 88 88 "8a, ,a8" 88, "8b, ,aa aa ]8I
  1330. 88 `8b `"Ybbd8"' 88 88 88 `"YbbdP"' "Y888 `"Ybbd8"' `"YbbdP"'
  1331. */
  1332. #if 0
  1333. enum E_REMOTE_FUNC_STUB
  1334. {
  1335. *E_REMOTE_FUNC_STUB_NEXT,
  1336. *E_REMOTE_FUNC_STUB_NAME,
  1337. CONST.pri /* SPEC ADDR */
  1338. CONST.alt /* FUNC ADDR */
  1339. JUMP /* STUB ADDR */
  1340. }
  1341. #endif
  1342. /**--------------------------------------------------------------------------**\
  1343. <summary>Remote_DoSearch</summary>
  1344. <param name="str[]">String to find.</param>
  1345. <param name="ptr">Start of the linked list.</param>
  1346. <returns>
  1347. -
  1348. </returns>
  1349. <remarks>
  1350. Finds a given string in a given list.
  1351. </remarks>
  1352. \**--------------------------------------------------------------------------**/
  1353. static stock Remote_DoSearch(const str[], ptr)
  1354. {
  1355. // Given a start pointer and a string, find the pointer before this string.
  1356. new
  1357. result;
  1358. while (ptr != -1)
  1359. {
  1360. #emit PUSH.C 0x7FFFFFFF
  1361. #emit PUSH.C 0
  1362. #emit PUSH.S str
  1363. #emit LOAD.S.pri ptr
  1364. #emit ADD.C 4
  1365. #emit PUSH.pri
  1366. #emit LREF.S.pri 0xFFFFFFEC
  1367. #emit SWAP.pri
  1368. // Compare the strings.
  1369. #emit PUSH.C 16
  1370. #emit SYSREQ.C strcmp
  1371. #emit STACK 20
  1372. #emit STOR.S.pri result
  1373. // Result found. Return.
  1374. if (!result) return ptr;
  1375. {}
  1376. // Didn't find it yet, try the next one.
  1377. #emit LREF.S.pri ptr
  1378. #emit STOR.S.pri ptr
  1379. }
  1380. return -1;
  1381. }
  1382. /**--------------------------------------------------------------------------**\
  1383. <summary>Remote_WriteStubCode</summary>
  1384. <returns>
  1385. -
  1386. </returns>
  1387. <remarks>
  1388. This rewrites itself to be the bulk of the call to "CallRemoteFunction". It
  1389. modifies the stack so that the parameters already pushed are the parameters
  1390. passed to the native function.
  1391. </remarks>
  1392. \**--------------------------------------------------------------------------**/
  1393. static stock Remote_WriteStubCode()
  1394. {
  1395. new
  1396. base,
  1397. ctx[AsmContext];
  1398. // Get this function.
  1399. #emit CONST.pri Remote_WriteStubCode
  1400. #emit LOAD.alt AMX_HEADER_COD
  1401. #emit ADD
  1402. #emit STOR.S.pri base
  1403. AsmInitPtr(ctx, base, 200); // Don't need any more than that.
  1404. // This function is jumped in to from "CallStoredFunction" via a
  1405. // per-function stub. Sadly, this means that the highly optimised 21 cell
  1406. // version of the code that I wrote won't work. It is now 30 in total,
  1407. // excluding the stub code (which is, and was, an extra 5 cells). This is
  1408. // called with "alt" = function name, "pri" = specifier string.
  1409. // Write the specifier string.
  1410. @emit STOR.S.pri 12
  1411. // Get the return address, and put the frame at the top of the stack.
  1412. @emit POP.pri
  1413. @emit SWAP.pri
  1414. @emit ADD.C (AMX_REAL_DATA + AMX_HEADER_COD) // Make absolute.
  1415. @emit STOR.pri (base + 26 * 4) // [JUMP ADDRESS]
  1416. // We have popped from the stack, but the frame pointer is still wrong. Get
  1417. // the parameter count and replace that location with the function pointer.
  1418. @emit LOAD.S.pri 8
  1419. @emit STOR.S.alt 8
  1420. // Update the parameter count.
  1421. @emit ADD.C 8
  1422. @emit STOR.pri (base + 24 * 4) // [STACK ADDRESS]
  1423. @emit ADD.C (-4)
  1424. @emit SWAP.pri
  1425. // Stored the parameter count and got the frame pointer.
  1426. @emit SCTRL 5
  1427. // Call "CallRemoteFunction".
  1428. @emit SYSREQ "CallRemoteFunction"
  1429. // End this function and return to the caller.
  1430. @emit STACK 0 // [STACK ADDRESS]
  1431. @emit JUMP 0 // [JUMP ADDRESS]
  1432. // Unlike most other self-modifying functions, this one does NOT call itself
  1433. // because it is called before it is required. Instead we just return to
  1434. // the caller that was preparing this setup in advance and pass it the
  1435. // absolute address of the start of the function (for jump purposes).
  1436. return base + AMX_REAL_DATA;
  1437. }
  1438. static stock Remote_RawStrpack(dest, const src[])
  1439. {
  1440. static
  1441. sRemoteStringsStart = -1,
  1442. sRemoteStringsEnd = -1;
  1443. new
  1444. len = (strlen(src) + 1) char * 4;
  1445. if (sRemoteStringsStart + len > sRemoteStringsEnd)
  1446. {
  1447. static
  1448. sPtr = -1;
  1449. if (sPtr == -1)
  1450. {
  1451. // Get the malloc base pointer.
  1452. #emit CONST.pri YSI_gMallocMemory
  1453. #emit STOR.pri sPtr
  1454. }
  1455. // No space to copy this string. Reallocate some memory.
  1456. sRemoteStringsStart = _:malloc(1024) * 4 + sPtr,
  1457. sRemoteStringsEnd = sRemoteStringsStart + 1024 * 4;
  1458. }
  1459. {}
  1460. #emit PUSH.C 1024
  1461. #emit PUSH.S src
  1462. #emit LOAD.pri sRemoteStringsStart
  1463. #emit PUSH.pri
  1464. #emit SREF.S.pri dest
  1465. #emit LOAD.S.alt len
  1466. #emit ADD
  1467. #emit STOR.pri sRemoteStringsStart
  1468. #emit PUSH.C 12
  1469. #emit SYSREQ.C strpack
  1470. #emit STACK 16
  1471. return 0;
  1472. }
  1473. static stock Remote_WriteJustSpec(/* & */ sptr, const spec[])
  1474. {
  1475. // Get the new write location.
  1476. new
  1477. ptr = CGen_GetCodeSpace();
  1478. // Add to the front of the list
  1479. AMX_Write(ptr, AMX_Read(sptr)),
  1480. AMX_Write(sptr, ptr),
  1481. // Store the specifier.
  1482. Remote_RawStrpack(ptr + 4, spec);
  1483. CGen_AddCodeSpace(2 * 4);
  1484. }
  1485. /**--------------------------------------------------------------------------**\
  1486. <summary>Remote_WriteSpecAndFunc</summary>
  1487. <param name="&fptr">Pointer in which to store the function.</param>
  1488. <param name="const func[]">Name of the function.</param>
  1489. <param name="sptr">Pointer to the stored specifier string.</param>
  1490. <param name="const spec[]">Usable specifier string.</param>
  1491. <returns>
  1492. A pointer to the start of the newly generated code.
  1493. </returns>
  1494. <remarks>
  1495. Generates a tiny function-specific stub that sets the values for the
  1496. function and specifier strings to pass to "CallRemoteFunction", and checks
  1497. for any empty strings - converting them to "NULL" instead.
  1498. </remarks>
  1499. \**--------------------------------------------------------------------------**/
  1500. static stock Remote_WriteSpecAndFunc(/* & */ fptr, const func[], sptr, const spec[])
  1501. {
  1502. new
  1503. ptr = CGen_GetCodeSpace();
  1504. // Add to the front of the list
  1505. AMX_Write(ptr, AMX_Read(fptr)),
  1506. AMX_Write(fptr, ptr),
  1507. // Store the function name.
  1508. Remote_RawStrpack(ptr + 4, func),
  1509. sptr = AMX_Read(sptr + 4),
  1510. AMX_Write(ptr + 8, sptr),
  1511. // Write the code.
  1512. CGen_AddCodeSpace(3 * 4); // Pointers.
  1513. new
  1514. ctx[AsmContext];
  1515. CGen_UseCodeSpace(ctx);
  1516. // Convert empty strings to "NULL" ("\0" -> "\1\0").
  1517. new
  1518. ss = 0;
  1519. for (new i = 0, j = strlen(spec); i != j; ++i)
  1520. {
  1521. if (spec[i] == 's')
  1522. {
  1523. if (!ss++) @emit CONST.alt ref(NULL) // Get the string pointer.
  1524. // A string, test and convert it.
  1525. @emit LREF.S.pri (i * 4 + 16) // Load the first character.
  1526. @emit JNZ.rel 8 // Not '\0', skip replacement.
  1527. @emit STOR.S.alt (i * 4 + 16) // Write the new pointer to "NULL".
  1528. }
  1529. }
  1530. @emit CONST.pri sptr
  1531. @emit CONST.alt AMX_Read(ptr + 4)
  1532. @emit JUMP YSI_g_sRemoteStub // Jump to the standard stub code.
  1533. // Skip over the newly written code and the stored specifier.
  1534. CGen_AddCodeSpace(6 * 4 + (ss ? (ss * 6 * 4 + 2 * 4) : 0));
  1535. // Return a direct SCTRL pointer to the code.
  1536. return ptr + 3 * 4 - AMX_HEADER_COD;
  1537. }
  1538. /*
  1539. 88888888888 88 88
  1540. 88 88 ""
  1541. 88 88
  1542. 88aaaaa 8b,dPPYba, ,adPPYba, ,adPPYba, ,adPPYb,88 88 8b,dPPYba, ,adPPYb,d8
  1543. 88""""" 88P' `"8a a8" "" a8" "8a a8" `Y88 88 88P' `"8a a8" `Y88
  1544. 88 88 88 8b 8b d8 8b 88 88 88 88 8b 88
  1545. 88 88 88 "8a, ,aa "8a, ,a8" "8a, ,d88 88 88 88 "8a, ,d88
  1546. 88888888888 88 88 `"Ybbd8"' `"YbbdP"' `"8bbdP"Y8 88 88 88 `"YbbdP"Y8
  1547. aa, ,88
  1548. "Y8bbdP"
  1549. */
  1550. /**--------------------------------------------------------------------------**\
  1551. <summary>Inline_DecodeComplex</summary>
  1552. <param name="from[]">Array of variable types.</param>
  1553. <param name="at">Type slot.</param>
  1554. <param name="len">Return for array sizes.</param>
  1555. <returns>
  1556. The next variable type stored in the bit array, and the length of arrays.
  1557. </returns>
  1558. <remarks>
  1559. Returns data from a bit array when the parameter could be basic (variable or
  1560. reference), or an array with a length (includes strings). This requries far
  1561. more complex code to decode as the lengths may span multiple cells, types
  1562. can't because they are always 2 bits and always start on an even bit.
  1563. </remarks>
  1564. \**--------------------------------------------------------------------------**/
  1565. static stock Inline_DecodeComplex(from[], &at, &len)
  1566. {
  1567. new
  1568. slot = at >>> 5,
  1569. // An encoded value is either 2 or 14 bits depending on type, so there
  1570. // is no way to ever have odd offsets. As a result there is no way for
  1571. // a type to span two cells, so there's no need for complex switches.
  1572. type = (from[slot] >>> (at & 0x1F)) & 0b11;
  1573. at += 2;
  1574. if (type & 0x01)
  1575. {
  1576. // Fast, cell boundary aware, length extraction. The length is stored
  1577. // -1 cell, so adjust for that too.
  1578. switch (at & 0x1F)
  1579. {
  1580. // Start of the (next) cell. For these two, doing "at += 2;"
  1581. // spilled the pointer over in to the next cell.
  1582. case 0: len = (from[slot + 1] & 0x0FFF) + 1;
  1583. #define I@(%0) case %0:len=((from[slot]>>>%0)&0x0FFF)+1;
  1584. // Whole length in one (current) cell.
  1585. I@(02)I@(04)I@(06)I@(08)I@(10)I@(12)I@(14)I@(16)I@(18)
  1586. #undef I@
  1587. // End of the cell.
  1588. case 20: len = (from[slot] >>> 20) + 1;
  1589. // Two cells (let the compiler do the maths for us).
  1590. #define I@(%0) case %0:len=((from[slot]>>>%0)|((from[slot+1]&(0xFFF>>>(32-%0)))<<(32-%0)))+1;
  1591. I@(22)I@(24)I@(26)I@(28)I@(30)
  1592. P:C(default: P:E("Odd offset in y_inline."););
  1593. #undef I@
  1594. }
  1595. at += 12;
  1596. }
  1597. return type;
  1598. }
  1599. static stock Inline_EncodeFormatString(const str[], ret[2])
  1600. {
  1601. new
  1602. size = 0,
  1603. complex = 0,
  1604. b0,
  1605. b1;
  1606. for (new i = 0; ; ++i)
  1607. {
  1608. switch (str[i])
  1609. {
  1610. case '\0': break;
  1611. case 'v':
  1612. {
  1613. // Add variable without length.
  1614. b1 = (b1 << 2) | (b0 >>> 30),
  1615. b0 = (b0 << 2) | INLINE_TYPE_REF,
  1616. ++size;
  1617. }
  1618. case 's', 'a':
  1619. {
  1620. if (str[i + 1] == '[')
  1621. {
  1622. new
  1623. j = i + 2,
  1624. len = 0;
  1625. while ('0' <= str[j] <= '9') len = (len * 10) + (str[j++] - '0');
  1626. --len,
  1627. b1 = (b1 << 14) | (b0 >>> 18);
  1628. // Add array with length.
  1629. if (str[i] == 'a') b0 = (b0 << 14) | ((len & 0xFFF) << 2) | INLINE_TYPE_ARR;
  1630. else b0 = (b0 << 14) | ((len & 0xFFF) << 2) | INLINE_TYPE_STR;
  1631. if (str[j] != ']') --j;
  1632. i = j,
  1633. size += 7,
  1634. complex = 1;
  1635. }
  1636. else
  1637. {
  1638. // Add variable without length.
  1639. b1 = (b1 << 2) | (b0 >>> 30),
  1640. b0 = (b0 << 2) | INLINE_TYPE_REF,
  1641. ++size;
  1642. }
  1643. }
  1644. default:
  1645. {
  1646. // Add variable without length.
  1647. b1 = (b1 << 2) | (b0 >>> 30),
  1648. b0 = (b0 << 2) | INLINE_TYPE_VAR,
  1649. ++size;
  1650. }
  1651. }
  1652. }
  1653. // Add total size.
  1654. ret[1] = (b1 << 6) | (b0 >>> 26),
  1655. ret[0] = (b0 << 6) | ((size & 0b00011111) << 1) | (complex ^ 1);
  1656. }
  1657. /*
  1658. 88 88
  1659. 88 ,d 88
  1660. 88 88 88
  1661. 88 8b,dPPYba, MM88MMM ,adPPYba, 8b,dPPYba, 8b,dPPYba, ,adPPYYba, 88
  1662. 88 88P' `"8a 88 a8P_____88 88P' "Y8 88P' `"8a "" `Y8 88
  1663. 88 88 88 88 8PP""""""" 88 88 88 ,adPPPPP88 88
  1664. 88 88 88 88, "8b, ,aa 88 88 88 88, ,88 88
  1665. 88 88 88 "Y888 `"Ybbd8"' 88 88 88 `"8bbdP"Y8 88
  1666. */
  1667. /**--------------------------------------------------------------------------**\
  1668. <summary>operator+</summary>
  1669. <param name="InlineRet:a">Dummy variable.</param>
  1670. <param name="b">Return value.</param>
  1671. <returns>
  1672. -
  1673. </returns>
  1674. <remarks>
  1675. This is a prefix function that takes some value and returns it to the
  1676. caller's caller. This makes "@return" in inline functions work.
  1677. </remarks>
  1678. \**--------------------------------------------------------------------------**/
  1679. stock InlineRet:operator+(InlineRet:a, _:b)
  1680. {
  1681. #pragma unused a
  1682. // Modify the return address.
  1683. #emit LOAD.S.pri 0
  1684. #emit ADD.C 4
  1685. #emit LOAD.I
  1686. #emit STOR.S.pri 4
  1687. // Get the last function's parameters.
  1688. #emit LOAD.S.pri 0
  1689. #emit ADD.C 8
  1690. #emit LOAD.I
  1691. #emit LOAD.S.alt 0
  1692. #emit ADD
  1693. #emit ADDR.alt 0
  1694. #emit SUB
  1695. #emit STOR.S.pri 8
  1696. // Load the return value.
  1697. #emit LOAD.S.pri b
  1698. // Copy the previous frame.
  1699. #emit LREF.S.alt 0
  1700. #emit STOR.S.alt 0
  1701. // Do the return
  1702. #emit RETN
  1703. return InlineRet:0;
  1704. }
  1705. stock InlineRet:operator+(InlineRet:a, Float:b)
  1706. {
  1707. #pragma unused a
  1708. // Modify the return address.
  1709. #emit LOAD.S.pri 0
  1710. #emit ADD.C 4
  1711. #emit LOAD.I
  1712. #emit STOR.S.pri 4
  1713. // Get the last function's parameters.
  1714. #emit LOAD.S.pri 0
  1715. #emit ADD.C 8
  1716. #emit LOAD.I
  1717. #emit LOAD.S.alt 0
  1718. #emit ADD
  1719. #emit ADDR.alt 0
  1720. #emit SUB
  1721. #emit STOR.S.pri 8
  1722. // Load the return value.
  1723. #emit LOAD.S.pri b
  1724. // Copy the previous frame.
  1725. #emit LREF.S.alt 0
  1726. #emit STOR.S.alt 0
  1727. // Do the return
  1728. #emit RETN
  1729. return InlineRet:0;
  1730. }
  1731. stock InlineRet:operator+(InlineRet:a, bool:b)
  1732. {
  1733. #pragma unused a
  1734. // Modify the return address.
  1735. #emit LOAD.S.pri 0
  1736. #emit ADD.C 4
  1737. #emit LOAD.I
  1738. #emit STOR.S.pri 4
  1739. // Get the last function's parameters.
  1740. #emit LOAD.S.pri 0
  1741. #emit ADD.C 8
  1742. #emit LOAD.I
  1743. #emit LOAD.S.alt 0
  1744. #emit ADD
  1745. #emit ADDR.alt 0
  1746. #emit SUB
  1747. #emit STOR.S.pri 8
  1748. // Load the return value.
  1749. #emit LOAD.S.pri b
  1750. // Copy the previous frame.
  1751. #emit LREF.S.alt 0
  1752. #emit STOR.S.alt 0
  1753. // Do the return
  1754. #emit RETN
  1755. return InlineRet:0;
  1756. }