impl.inc 62 KB

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