y_inline.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  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 AMX 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. 15/11/11:
  51. Changed the precedence of "using" types.
  52. 19/09/11:
  53. First version
  54. \*----------------------------------------------------------------------------*/
  55. #include "internal\y_version"
  56. #include "internal\y_funcinc"
  57. #include "y_amx"
  58. #include "y_utils"
  59. #include "y_malloc"
  60. #include "y_hooks"
  61. #if defined YSI_MALLOC_SECURE
  62. #error y_inline does not work with "YSI_MALLOC_SECURE" defined.
  63. #endif
  64. // "with inline X"
  65. // "with public X"
  66. // %0 = " in" or " pub" (ignored).
  67. // "%1 = "ne X" or "c X" (makes a macro).
  68. #define using%0) callback_tag:@Ik:@Il:%0)
  69. // Get ONLY this ONE parameter (this is a VERY important stage)!
  70. #define @Ik:@Il:%0, @Ip:@Iq:@Im:@Io:@Iw:|||%0|||,
  71. #define @Il:%0) @Ip:@Iq:@Im:@Io:@Iw:|||%0|||)
  72. // You can use "using InlineFunc" or the faster "using inline InlineFunc".
  73. /*#define @Ip:@Iq:@Im:@Io:@Iw:|||%0anonymous%1||| @Iu:@Iv:%0NULL%1||||
  74. #define @Iq:@Im:@Io:@Iw:|||%0callback%1||| @Ir:@Is:%1||||
  75. #define @Im:@Io:@Iw:|||%0inline%1||| @Iu:@Iv:%0%1||||
  76. #define @Io:@Iw:|||%0public%1||| @Ir:@Is:%1||||
  77. #define @Iw:|||%1||| @In:@It:%1||||*/
  78. #define @Ip:@Iq:@Im:@Io:@Iw:|||%0inline%1||| @Iu:@Iv:%0%1||||
  79. #define @Iq:@Im:@Io:@Iw:|||%0public%1||| @Ir:@Is:%1||||
  80. #define @Im:@Io:@Iw:|||%0anonymous%1||| @Iu:@Iv:%0NULL%1||||
  81. #define @Io:@Iw:|||%0callback%1||| @Ir:@Is:%1||||
  82. #define @Iw:|||%1||| @In:@It:%1||||
  83. // Callbacks with "On" in the name (often overidden by y_hooks and ALS).
  84. #define @Ir:@Is:%0On%1|||| @In:@It:#%0"On"#%1||||
  85. #define @Is:%0|||| @In:@It:#%0|||| //Using_unknown_callback
  86. // Callbacks with additional parameters (MUST have matching parameters (y_ini)).
  87. #define @In:@It:%0(%1)||||%2) %0%2,.bExtra=true,.extra=%1)
  88. #define @It:%0|||| %0
  89. // Inline function surpressing closures.
  90. #define @Iu:@Iv:%0$%1|||| (J@=0,_:@In:@It:%1|||| _Y_INLINE_END
  91. #define @Iv:%1|||| (J@=1,_:@In:@It:%1|||| _Y_INLINE_END
  92. // Defer adding the close bracket till after other macros have run.
  93. #define _Y_INLINE_END )
  94. #define INLINE_LOOP_PATTERN_0 0xA1E7C013
  95. #define INLINE_LOOP_PATTERN_1 0x42424242
  96. #define INLINE_LOOP_PATTERN_2 0x13C0E7A1
  97. #define INLINE_LOOP_PATTERN_3 0x21495359
  98. #define INLINE_LOOP_PATTERNS INLINE_LOOP_PATTERN_0,INLINE_LOOP_PATTERN_1,INLINE_LOOP_PATTERN_2,INLINE_LOOP_PATTERN_3
  99. // This code uses a specialisation of my "tag macros" technique, embedding the
  100. // macro names in to a string instead of using them as tags. This does mean
  101. // that they will likely end up in the final AMX (unlike tag macros) but this
  102. // seems to be the only way to get this to work that I can see as there is no
  103. // variable reading involved.
  104. // The "inline" macro only has ":...", whereas the "anonymous" macro has ":...:"
  105. // because "inline" gets its second colon later on after all the parameter tag-
  106. // style macros. The structure for these extra cells is:
  107. //
  108. // 0 - Colon.
  109. // 1 - Pointer to next function name.
  110. // 2 - Pointer the the function.
  111. // 3 - Cells in the enclosing function at this point (passed and declared).
  112. // 4 - Parameters format.
  113. //
  114. #define inline%0(%1) new %0__;static const%0[]=#%0":..."#_YI@CP;for(new%1,,;Inline_Loop(INLINE_LOOP_PATTERNS,%0__,%0);)
  115. #define callback:%0) const callback_tag:callback_check:%0£££
  116. #define callback_tag:callback_check:%0,%1£££ callback_tag:%0[],%1)
  117. #define callback_check:%0£££ %0[])
  118. #define anonymous%0(%1) Q@=#_YI@CA;for(new%1,,;Inline_Loop(INLINE_LOOP_PATTERNS,%0__,":...:"#);)
  119. #define _YI@CP;for(new%0,%1; @Ia#@Ib#@Ic#@Id#:;for(new @Iz|||%0|||%1;
  120. #define _YI@CA;for(new%0,%1; @If#@Ig#@Ih#@Ii#:;for(new @Iz|||%0|||%1;
  121. // Detect 0 parameters.
  122. #define @Ia#@Ib#@Ic#@Id#:;for(new%0||||||,;%1;) :;for(;%1;)
  123. // Detect strings (no array support YET).
  124. #define @Ib#@Ic#@Id#%9;for(new%0|||%1string:%2[%3]|||%4,%5; @Ib#@Ic#@Id#%9s;for(new%0,%2[YSI_MAX_STRING]|||%4|||%5;
  125. // Detect end of line.
  126. #define @Ic#@Id#%9;for(new%0|||%2|||%4,%5; @Ib#@Ic#@Id#%9i;for(new%0,%2|||%4|||%5;
  127. // Detect everything else.
  128. #define @Id#%9;for(new%0||||||; %9;for(new %0;
  129. // Drop the leading comma on the parameter list.
  130. #define @Iz,
  131. // Detect 0 parameters.
  132. #define @If#@Ig#@Ih#@Ii#:;for(new%0||||||,;%1;) :;for(;%1;)
  133. // Detect strings (no array support YET).
  134. #define @Ig#@Ih#@Ii#%9;for(new%0|||%1string:%2[%3]|||%4,%5;Inline_Loop(%6#%7) @Ig#@Ih#@Ii#%9;for(new%0,%2[YSI_MAX_STRING]|||%4|||%5;Inline_Loop(%6#%7s)
  135. // Detect end of line.
  136. #define @Ih#@Ii#%9;for(new%0|||%2|||%4,%5;Inline_Loop(%6#%7) @Ig#@Ih#@Ii#%9;for(new%0,%2|||%4|||%5;Inline_Loop(%6#%7i)
  137. // Detect everything else.
  138. #define @Ii#%9;for(new%0||||||; ;for(new%0;
  139. // Drop the leading comma on the parameter list.
  140. #define @Iz,
  141. static stock
  142. YSI_g_sFirstFunc = -1,
  143. YSI_g_sInInline = 0,
  144. //YSI_g_sLastFunc = -1,
  145. YSI_g_sReturn;
  146. enum e_CALLBACK_FLAGS (<<= 1)
  147. {
  148. // All this is required to correctly design the call stack.
  149. e_CALLBACK_FLAGS_PUSHED = 0x000000FF, // Parameters pushed to caller.
  150. e_CALLBACK_FLAGS_CREATED = 0x0FFFFF00, // Data size declared in caller.
  151. e_CALLBACK_FLAGS_PUBLIC = 0x10000000 // Is a public function.
  152. //e_CALLBACK_FLAGS_INLINE // Is an inline function.
  153. }
  154. enum E_CALLBACK_DATA
  155. {
  156. e_CALLBACK_FLAGS:E_CALLBACK_DATA_FLAGS,
  157. E_CALLBACK_DATA_POINTER, // Pointer to the function.
  158. E_CALLBACK_DATA_FORMAT, // Compressed destination format.
  159. Alloc:E_CALLBACK_DATA_ALLOC // Where our closure is stored.
  160. }
  161. static stock Inline_DoFormat(data[])
  162. {
  163. // This function encodes the data format in to a single cell. The format is:
  164. //
  165. // 1111111001
  166. //
  167. // Leading 1s indicate no data. The 0 immediately following the leading 1s
  168. // indicates the start of the format section (but is not PART of the format
  169. // section). The remaining bits represent either strings (1) or non-strings
  170. // (0). For example "(a, string:b, c)" would be:
  171. //
  172. // 1..10010
  173. //
  174. // Where "1..1" indicates full-cell padding of 1s. From this it is known that
  175. // the function takes three parameters: non-string, string, non-string. In
  176. // addition, as strings in inline functions MUST use the "string:" tag, it is
  177. // known that ALL strings will ALWAYS be 128 (or "YSI_MAX_STRING") cells big.
  178. new
  179. pos = strfind(data, ":"),
  180. //len = 0, //strlen(data),
  181. //bit = 1,
  182. total = 1;
  183. //P:C(if (len - pos - 1 > 30) P:E("Inline functions only support up to 30 parameters"););
  184. if (pos != -1)
  185. {
  186. for ( ; ; )
  187. {
  188. // Now matchs the compile-time code much closer.
  189. switch (data[++pos])
  190. {
  191. case '\0':
  192. {
  193. break;
  194. }
  195. case 's':
  196. {
  197. total <<= 1;
  198. }
  199. default:
  200. {
  201. total = total << 1 | 1;
  202. }
  203. }
  204. }
  205. }
  206. // Store the compressed format, also instantly end the string.
  207. data[0] = ~total;
  208. data[1] = '\0';
  209. return 1;
  210. }
  211. static stock Inline_FindFunction(const data[], const name[], address)
  212. {
  213. new
  214. value,
  215. len = strlen(name),
  216. tmp,
  217. candidate = cellmax,
  218. ret = -1;
  219. #emit LOAD.S.pri data
  220. #emit STOR.S.pri value
  221. // Check the "pointer" is valid.
  222. P:3("Inline_FindFunction called: %08x, %s, %s", value, data, name);
  223. while (value != -1)
  224. {
  225. if (strcmp(name, data, false, len) || data[len] != ':')
  226. {
  227. value = data[strlen(data) - 4];
  228. }
  229. else
  230. {
  231. /*printf("format = %04x%04x", data[len + 4] >>> 16, data[len + 4] & 0xFFFF);
  232. printf("%d", data[len + 1]);
  233. printf("%d", data[len + 2]);
  234. printf("%d", data[len + 3]);
  235. printf("%d", data[len + 4]);*/
  236. // Found a candidate. Now only finds the closest match BEFORE the call.
  237. tmp = address - data[len + 2];
  238. //if (0 < tmp < candidate)
  239. if (-5000 < tmp < 5000)
  240. {
  241. // Constrain the checks to +-5000 because of square integer limits and
  242. // to help reduce clashes.
  243. if ((tmp *= tmp) < candidate)
  244. {
  245. ret = value;
  246. candidate = tmp;
  247. printf("candidate: %x", ret);
  248. }
  249. }
  250. value = data[len + 1];
  251. }
  252. // Move on to the next "pointer".
  253. #emit LOAD.S.pri value
  254. #emit STOR.S.pri data
  255. }
  256. return ret;
  257. }
  258. static stock Inline_FindAnonymous(const data[], address)
  259. {
  260. new
  261. value,
  262. tmp,
  263. candidate = cellmax,
  264. ret = -1;
  265. #emit LOAD.S.pri data
  266. #emit STOR.S.pri value
  267. // Check the "pointer" is valid.
  268. while (value != -1)
  269. {
  270. // Check if this is anonymous.
  271. if (data[0] == ':')
  272. {
  273. // Found a candidate.
  274. tmp = data[2] - address;
  275. // Make sure this is the closest anonymous function AFTER the return. We
  276. // don't need "0 <=" as it will never be INSTANTLY after the return
  277. // address due to the required "Inline_Loop" call.
  278. if (0 < tmp < candidate)
  279. {
  280. ret = value;
  281. candidate = tmp;
  282. }
  283. }
  284. value = data[strlen(data) - 3];
  285. // Move on to the next "pointer".
  286. #emit LOAD.S.pri value
  287. #emit STOR.S.pri data
  288. }
  289. return ret;
  290. }
  291. forward _Inline_FixCompiler@@();
  292. public _Inline_FixCompiler@@()
  293. {
  294. // Call the function above at least once so the address exists in tables. But
  295. // never ACTUALLY call it at run-time (don't call this public function).
  296. Inline_DoFormat("");
  297. Inline_FindFunction("", "", 0);
  298. Inline_FindAnonymous("", 0);
  299. //memcpy("", "", 0, 0, 0);
  300. }
  301. hook OnScriptInit()
  302. {
  303. static
  304. sSearch[] =
  305. {
  306. AMX_PUSH_C, INLINE_LOOP_PATTERN_3,
  307. AMX_PUSH_C, INLINE_LOOP_PATTERN_2,
  308. AMX_PUSH_C, INLINE_LOOP_PATTERN_1,
  309. AMX_PUSH_C, INLINE_LOOP_PATTERN_0,
  310. AMX_PUSH_C, 0x18,
  311. AMX_CALL
  312. };
  313. new
  314. addr,
  315. data,
  316. func,
  317. last = -1;
  318. while (AMX_TraceCode(sSearch, addr, func))
  319. {
  320. // Get the function return address (make sure "pri" is always non-zero).
  321. func = addr + (12 * 4);
  322. //printf("stored function at %x", func);
  323. // Get the address of the last parameter.
  324. addr += (AMX_HEADER_COD - 12);
  325. // Get the value of the last parameter.
  326. #emit LREF.S.pri addr
  327. #emit STOR.S.pri data
  328. // PERFECT! This assembly code worked FIRST TIME to correctly display
  329. // both the address and contents of the loaded string! Amazingly it
  330. // worked for anonymous functions too...
  331. //#emit PUSH.S data
  332. //printf("data = %08x, %s", data);
  333. //#emit POP.pri
  334. // Anyway, now we need to see if this is a named or anonymous function
  335. // and plan accordingly. We don't need all the complex code of the
  336. // previous inline version. If we get a function call which takes an
  337. // anonymous function, just assume it is the next one found in the list
  338. // of stored inline function addresses. This has the HUGE added
  339. // advantage of allowing small bits of extra code to appear between the
  340. // function call and the inline function - i.e. we can allow return
  341. // values and allsorts now (including having functions taking anonymous
  342. // functions themselves being used as parameters).
  343. //new
  344. // pos = strfind(
  345. static const
  346. scSearch[] = ":";
  347. new
  348. pos = 0;
  349. #emit PUSH.C 0
  350. #emit PUSH.C 0
  351. #emit PUSH.C scSearch
  352. #emit PUSH.S data
  353. #emit PUSH.C 16
  354. #emit SYSREQ.C strfind
  355. #emit STOR.S.pri pos
  356. #emit STACK 20
  357. if (pos != -1)
  358. {
  359. if (last == -1)
  360. {
  361. YSI_g_sFirstFunc = data;
  362. }
  363. else
  364. {
  365. #emit LOAD.S.pri data
  366. #emit SREF.S.pri last
  367. }
  368. // Equivalent to: "data[pos + 1] = -1;" (1 cell = 4 bytes).
  369. data += pos * 4 + 4;
  370. #emit CONST.pri 0xFFFFFFFF
  371. #emit SREF.S.pri data
  372. // Equivalent to: "data[pos + 2] = func;"
  373. last = data;
  374. data += 4;
  375. #emit LOAD.S.pri func
  376. #emit SREF.S.pri data
  377. // Now find and compress the format specifier (backwards).
  378. // Now compress the format in to a single cell (up to 32 parameters).
  379. #emit LOAD.S.pri data
  380. #emit ADD.C 8//4
  381. #emit PUSH.pri
  382. #emit PUSH.C 4
  383. // Using "CALL Inline_DoFormat" doesn't work, so do the next best thing.
  384. #emit LCTRL 6
  385. #emit ADD.C 28
  386. #emit PUSH.pri
  387. #emit CONST.pri Inline_DoFormat
  388. #emit SCTRL 6
  389. }
  390. // Move on to find the next value.
  391. addr -= (AMX_HEADER_COD - 16);
  392. }
  393. }
  394. stock Inline_Loop(p0, p1, p2, p3, &__yil, volatile const format[])
  395. {
  396. #pragma unused p0, p1, p2, p3
  397. //#emit LOAD.S.pri 4
  398. //#emit STOR.S.pri p0
  399. //printf("ret: %d %d", p0, YSI_g_sInInline);
  400. if (__yil)
  401. {
  402. /*#emit LOAD.S.alt 0
  403. #emit MOVE.pri
  404. #emit ADD.C 4
  405. #emit LOAD.I
  406. #emit XCHG
  407. #emit LOAD.I
  408. #emit STOR.S.pri p0
  409. #emit MOVE.pri
  410. #emit STOR.S.pri p1
  411. printf("%d %d", p0, p1);*/
  412. // Somehow I need to check
  413. #emit LOAD.S.alt 0
  414. #emit MOVE.pri
  415. #emit ADD.C 4
  416. #emit LOAD.I
  417. #emit XCHG
  418. #emit LOAD.I
  419. #emit SCTRL 5
  420. #emit MOVE.pri
  421. #emit SCTRL 6
  422. }
  423. __yil = 1;
  424. static const
  425. scSearch[] = ":";
  426. // This function needs to be modified to store the stack size at this point
  427. // and write it to the relevant slot (easy since the relevant slot is
  428. // passed). I know "volatile const" makes no sense, but "const" is for the
  429. // compiler, "volatile" is to show that really it does change.
  430. #emit LOAD.S.pri 0
  431. #emit ADD.C 8
  432. #emit LOAD.I
  433. #emit PUSH.pri
  434. // Get the local variable sizes. Need to allocate the data somewhere. First
  435. #emit LCTRL 5
  436. #emit LOAD.S.alt 0
  437. // Subtract the parameters passed to this function.
  438. #emit ADD.C 36 // 6 * 4 + 12
  439. #emit SUB.alt
  440. #emit PUSH.pri
  441. // Do strfind.
  442. #emit PUSH.C 0
  443. #emit PUSH.C 0
  444. #emit PUSH.C scSearch
  445. #emit PUSH.S format
  446. #emit PUSH.C 16
  447. #emit SYSREQ.C strfind
  448. #emit STACK 20
  449. // Save the data.
  450. #emit CONST.alt 4
  451. #emit SMUL
  452. #emit ADD.C 12
  453. #emit LOAD.S.alt format
  454. #emit ADD
  455. #emit STOR.S.pri format
  456. #emit POP.alt
  457. #emit SHL.C.alt 6
  458. #emit POP.pri
  459. #emit SHR.C.pri 2
  460. #emit ADD
  461. #emit SREF.S.pri format
  462. return 0;
  463. }
  464. stock Callback_Get(callback:name, result[E_CALLBACK_DATA], expect = -1)
  465. {
  466. new
  467. func,
  468. num,
  469. pos;
  470. P:2("Callback_Get called: %s %04x%04x", _:name, expect >>> 16, expect & 0xFFFF);
  471. if (isnull(_:name))
  472. {
  473. // Anonymous inline. Need to find the next available inline function based
  474. // on the return address of the calling function.
  475. // Get the return address.
  476. #emit LOAD.S.pri 0
  477. #emit ADD.C 4
  478. #emit LOAD.I
  479. // Call the function.
  480. #emit PUSH.pri
  481. #emit PUSH.S name
  482. #emit PUSH YSI_g_sFirstFunc
  483. #emit PUSH.C 8
  484. #emit LCTRL 6
  485. #emit ADD.C 28
  486. #emit PUSH.pri
  487. #emit CONST.pri Inline_FindAnonymous
  488. #emit SCTRL 6
  489. #emit STOR.S.pri func
  490. if (func == -1)
  491. {
  492. return 0;
  493. }
  494. // Save the data.
  495. func += 2 * 4;
  496. #emit LREF.S.pri func
  497. #emit STOR.S.pri pos
  498. result[E_CALLBACK_DATA_POINTER] = pos;
  499. // Save the function parameters.
  500. ++func;
  501. #emit LREF.S.pri func
  502. #emit STOR.S.pri pos
  503. result[E_CALLBACK_DATA_FLAGS] = e_CALLBACK_FLAGS:pos;
  504. ++func;
  505. #emit LREF.S.pri func
  506. #emit STOR.S.pri pos
  507. result[E_CALLBACK_DATA_FORMAT] = pos;
  508. if (expect != -1 && pos != expect)
  509. {
  510. P:E("Format specifier didn't match on anonymous function");
  511. }
  512. new
  513. mask = 0x80000000;
  514. // Skip leading 1s.
  515. while (pos & mask)
  516. {
  517. mask >>>= 1;
  518. }
  519. // Skip delimiting 0.
  520. mask >>>= 1;
  521. while (mask)
  522. {
  523. if (pos & mask)
  524. {
  525. num += YSI_MAX_STRING;
  526. }
  527. else
  528. {
  529. ++num;
  530. }
  531. mask >>>= 1;
  532. }
  533. }
  534. else
  535. {
  536. pos = strfind(name, ":");
  537. P:5("Callback_Get: %d, %d, %d, %d, %04x%04x", _:name[pos + 1], _:name[pos + 2], _:name[pos + 3] >>> 8, _:name[pos + 3] & 0xFF, _:name[pos + 4] >>> 16, _:name[pos + 4] & 0xFFFF);
  538. if (pos == -1)
  539. {
  540. if (AMX_GetPublicPointer(0, pos, name))
  541. {
  542. // Public function, use standard callback techniques (well, psudo-
  543. // standard, just store the address and use SCTRL manipulation).
  544. result[E_CALLBACK_DATA_POINTER] = pos;
  545. result[E_CALLBACK_DATA_FLAGS] = e_CALLBACK_FLAGS_PUBLIC;
  546. result[E_CALLBACK_DATA_FORMAT] = expect;
  547. return 1;
  548. }
  549. else
  550. {
  551. P:5("Callback_Get: Not got");
  552. // Get the caller frame.
  553. #emit LOAD.S.pri 0
  554. // Get the caller return.
  555. #emit ADD.C 4
  556. #emit LOAD.I
  557. // Now find the closest item with the correct name. Hopefully 99% of
  558. // the time there will only be one function with this name anywhere
  559. // NEAR the return address, so we can use that one. Otherwise we will
  560. // just have to hope that the closest is correct (maybe add a check to
  561. // see if it's too close, and if so alert the user).
  562. #emit PUSH.pri
  563. #emit PUSH.S name
  564. #emit PUSH YSI_g_sFirstFunc
  565. #emit PUSH.C 12
  566. #emit LCTRL 6
  567. #emit ADD.C 28
  568. #emit PUSH.pri
  569. #emit CONST.pri Inline_FindFunction
  570. #emit SCTRL 6
  571. #emit STOR.S.pri func
  572. // So now "func" is the address of the handle to the nearest data we can
  573. // extract all the relevant data.
  574. if (func == -1)
  575. {
  576. P:5("Callback_Get: inline/public not found");
  577. return 0;
  578. }
  579. P:5("Callback_Get: inline/public found: %08x", func);
  580. // Save the function pointer.
  581. func += strlen(name) * 4 + 2 * 4;
  582. P:5("Callback_Get: inline/public found: %08x", func);
  583. //#emit LREF.S.pri func
  584. pos = 444;
  585. #emit LOAD.S.pri func
  586. #emit LOAD.I
  587. #emit STOR.S.pri pos
  588. printf("%d", pos);
  589. result[E_CALLBACK_DATA_POINTER] = pos;
  590. // Save the function parameters.
  591. ++func;
  592. #emit LREF.S.pri func
  593. #emit STOR.S.pri pos
  594. printf("%d", pos);
  595. result[E_CALLBACK_DATA_FLAGS] = e_CALLBACK_FLAGS:pos;
  596. // Save the function format.
  597. ++func;
  598. --pos;
  599. #emit LREF.S.pri func
  600. #emit STOR.S.pri pos
  601. printf("%d", pos);
  602. result[E_CALLBACK_DATA_FORMAT] = pos;
  603. if (expect != -1 && pos != expect)
  604. {
  605. P:E("Format specifier didn't match on inline function %s: %04x%04x != %04x%04x", name, pos >>> 16, pos & 0xFFFF, expect >>> 16, expect & 0xFFFF);
  606. }
  607. new
  608. mask = 0x80000000;
  609. // Skip leading 1s.
  610. while (pos & mask)
  611. {
  612. mask >>>= 1;
  613. }
  614. // Skip delimiting 0.
  615. mask >>>= 1;
  616. while (mask)
  617. {
  618. if (pos & mask)
  619. {
  620. num += YSI_MAX_STRING;
  621. }
  622. else
  623. {
  624. ++num;
  625. }
  626. mask >>>= 1;
  627. }
  628. }
  629. }
  630. else
  631. {
  632. // Named and qualified inline function. Should also include the correct
  633. // addresses. By FAR the fastest method as we already have all the data.
  634. result[E_CALLBACK_DATA_POINTER] = name[pos + 2];
  635. result[E_CALLBACK_DATA_FLAGS] = e_CALLBACK_FLAGS:name[pos + 3];
  636. new
  637. form = name[pos + 4];
  638. result[E_CALLBACK_DATA_FORMAT] = form;
  639. if (expect != -1 && form != expect)
  640. {
  641. P:E("Format specifier didn't match on inline function %s", name);
  642. }
  643. // Get the size of inline function parameters:
  644. new
  645. mask = 0x80000000;
  646. // Skip leading 1s.
  647. while (form & mask)
  648. {
  649. mask >>>= 1;
  650. }
  651. // Skip delimiting 0.
  652. mask >>>= 1;
  653. while (mask)
  654. {
  655. if (form & mask)
  656. {
  657. num += YSI_MAX_STRING;
  658. }
  659. else
  660. {
  661. ++num;
  662. }
  663. mask >>>= 1;
  664. }
  665. }
  666. }
  667. // Now we need to somehow store all this data somewhere (including, for
  668. // speed, the extra data involved in calling a function). Here "pos" is the
  669. // number of bytes pushed to the owning function.
  670. result[E_CALLBACK_DATA_FLAGS] -= e_CALLBACK_FLAGS:(num << 8);
  671. pos = _:result[E_CALLBACK_DATA_FLAGS];
  672. // Get the size of the closure.
  673. func = (pos & 0xFF);
  674. pos = (pos >>> 8); // - num;
  675. func = func + pos + 3;
  676. new
  677. Alloc:alloc = malloc(func);
  678. if (alloc == NO_ALLOC)
  679. {
  680. return 0;
  681. }
  682. result[E_CALLBACK_DATA_ALLOC] = alloc;
  683. // Now we need to copy the data from the previous-but-one frame to this
  684. // allocated location. Copy the whole lot, including passed parameters.
  685. #emit LOAD.S.pri pos
  686. #emit SMUL.C 4
  687. #emit MOVE.alt
  688. #emit LOAD.S.pri 0
  689. #emit LOAD.I
  690. #emit SUB
  691. #emit STOR.S.pri name
  692. memcpy(YSI_gMallocMemory[_:alloc], name, 0, func * 4, func);
  693. return 1;
  694. }
  695. stock Callback_Release(const input[E_CALLBACK_DATA])
  696. {
  697. free(input[E_CALLBACK_DATA_ALLOC]);
  698. }
  699. stock Callback_Call(const result[E_CALLBACK_DATA], GLOBAL_TAG_TYPES:...)
  700. {
  701. // Call the function with the given data. We need some serious stack
  702. // manipulation skills in here to make it all work.
  703. if (result[E_CALLBACK_DATA_FLAGS] & e_CALLBACK_FLAGS_PUBLIC)
  704. {
  705. // I think I've got an even better way. NOPE! None of this code will
  706. // work because all the parameters are passed by reference, not by
  707. // value! This is VERY VERY bad! D'oh! Good thing I kept a copy of
  708. // the old code! Shame, this would have been very elegant. Sweet, it
  709. // seemed to work as well! Maybe I could just do some similar in-line
  710. // variable modifications.
  711. new
  712. par,
  713. pointer = result[E_CALLBACK_DATA_POINTER],
  714. mask = result[E_CALLBACK_DATA_FORMAT];
  715. // Destroy one parameter.
  716. //#emit PUSH.S 8
  717. #emit LOAD.S.alt 8
  718. #emit PUSH.alt
  719. //#emit ADD.C 0xFFFFFFFC
  720. //#emit PUSH.pri
  721. // Move the return address.
  722. #emit LOAD.S.pri 4
  723. #emit STOR.S.pri 8
  724. // Move the frame.
  725. #emit LOAD.S.pri 0
  726. #emit STOR.S.pri 4
  727. // Fix the parameters.
  728. #emit LCTRL 5
  729. #emit ADD
  730. #emit ADD.C 12
  731. #emit STOR.S.pri par
  732. // Get the jump address.
  733. //while (mask != ~1)
  734. // If no format has been provided, just guess and pass every parameter
  735. // by reference (as they are passed to here).
  736. while (mask != -1)
  737. {
  738. par -= 4;
  739. if (!(mask & 1))
  740. {
  741. #emit LREF.S.pri par
  742. #emit LOAD.I
  743. #emit SREF.S.pri par
  744. }
  745. mask >>= 1;
  746. }
  747. //#emit LOAD.S.pri 8
  748. #emit POP.pri
  749. #emit ADD.C 0xFFFFFFFC
  750. #emit STOR.S.pri 12
  751. //#emit ADD.C 4
  752. //#emit ADD.C 4
  753. //#emit MOVE.alt
  754. #emit LOAD.S.alt pointer
  755. // Mangle the stack (no variables from here).
  756. #emit LCTRL 5
  757. #emit ADD.C 4
  758. #emit SCTRL 4
  759. #emit SCTRL 5
  760. // Jump to new code (after "PROC").
  761. #emit MOVE.pri
  762. #emit ADD.C 4
  763. #emit SCTRL 6
  764. // Will never be called.
  765. //#emit RETN
  766. }
  767. else
  768. {
  769. new
  770. size = _:result[E_CALLBACK_DATA_FLAGS],
  771. num = 0,
  772. stack,
  773. mask = 0x80000000,
  774. addr,
  775. tmp;
  776. //YSI_g_sInInline = result[E_CALLBACK_DATA_POINTER];
  777. // ininline = YSI_g_sInInline;
  778. YSI_g_sInInline = result[E_CALLBACK_DATA_POINTER];
  779. size = ((size & 0xFF) + (size >>> 8) + 3) * 4;
  780. #emit LCTRL 4
  781. #emit STOR.S.pri stack
  782. #emit LOAD.S.alt size
  783. #emit SUB
  784. #emit STOR.S.pri addr
  785. // Add more data for additional parameters.
  786. #emit SCTRL 4
  787. //#emit LCTRL 4
  788. size = result[E_CALLBACK_DATA_FORMAT];
  789. #emit LCTRL 5
  790. #emit ADD.C 16
  791. #emit STOR.S.pri tmp
  792. // OK, now the fun bit!
  793. while (size & mask)
  794. {
  795. mask >>>= 1;
  796. }
  797. mask >>>= 1;
  798. while (mask)
  799. {
  800. if (size & mask)
  801. {
  802. num += YSI_MAX_STRING;
  803. addr -= YSI_MAX_STRING * 4;
  804. //printf("copying string");
  805. #emit LOAD.S.pri addr
  806. #emit SCTRL 4
  807. // Copy the data.
  808. #emit PUSH.C 130
  809. #emit PUSH.C 520
  810. #emit PUSH.C 0
  811. #emit LREF.S.pri tmp
  812. #emit PUSH.pri
  813. #emit PUSH.S addr
  814. #emit PUSH.C 20
  815. #emit SYSREQ.C memcpy
  816. #emit STACK 24
  817. //printf("finished");
  818. }
  819. else
  820. {
  821. num += 1;
  822. addr -= 1 * 4;
  823. #emit LREF.S.pri tmp
  824. #emit LOAD.I
  825. #emit PUSH.pri
  826. }
  827. mask >>>= 1;
  828. tmp += 4;
  829. }
  830. #emit LCTRL 5
  831. #emit STOR.S.pri tmp
  832. num *= 4;
  833. addr += num;
  834. // "addr" now contains the params stack address, "stack" contains the
  835. // starting stack address. This code technically pushes an incorrect
  836. // destination size (it's 4x too big), but as the bytes to copy is
  837. // smaller this is not important.
  838. // Set the frame pointer.
  839. size = _:result[E_CALLBACK_DATA_FLAGS];
  840. #emit LOAD.S.pri size
  841. #emit SHR.C.pri 8
  842. #emit SHL.C.pri 2 // NOT SHR 6
  843. #emit LOAD.S.alt addr
  844. #emit ADD
  845. #emit STOR.S.pri tmp
  846. // Copy the data.
  847. size = ((size & 0xFF) + (size >>> 8) + 3) * 4;
  848. num = _:result[E_CALLBACK_DATA_ALLOC];
  849. #emit LOAD.S.pri size
  850. #emit PUSH.pri
  851. #emit PUSH.pri
  852. #emit PUSH.C 0
  853. #emit CONST.alt YSI_gMallocMemory
  854. #emit LOAD.S.pri num
  855. #emit IDXADDR
  856. #emit PUSH.pri
  857. #emit PUSH.S addr
  858. #emit PUSH.C 20
  859. #emit SYSREQ.C memcpy
  860. #emit STACK 24
  861. // Store the return frame.
  862. #emit LOAD.S.alt tmp
  863. #emit LCTRL 5
  864. #emit STOR.I
  865. #emit MOVE.pri
  866. #emit ADD.C 4
  867. #emit MOVE.alt
  868. // Get the return address and call the function.
  869. #emit LCTRL 6
  870. #emit ADD.C 48 // 8
  871. #emit STOR.I // 12
  872. #emit LOAD.alt YSI_g_sInInline // 20
  873. #emit LOAD.S.pri tmp // 28
  874. #emit SCTRL 5 // 36
  875. #emit MOVE.pri // 40
  876. #emit SCTRL 6 // 48
  877. // Restore the stack.
  878. //printf("one");
  879. #emit LOAD.S.pri stack
  880. #emit SCTRL 4
  881. //printf("two");
  882. //YSI_g_sInInline = ininline;
  883. }
  884. }
  885. // HOPEFULLY will derive the compressed format specifier for a function, with
  886. // anything not "s" zero.
  887. //#define _S<%0> (-1&_:@Rx:@Ry:@Rw:@Rv:@Ru:(0,%0,0))
  888. #define _F<%0> (-1&_:~@Rx:@Ry:@Rv:@Ru:@Rw:(1,%0))
  889. #define @Rx:@Ry:@Rv:@Ru:@Rw:(%9,s%0) @Rx:@Ry:@Rv:@Ru:@Rw:((%9)<<1,%0)
  890. #define @Ry:@Rv:@Ru:@Rw:(%9,i%0) @Rx:@Ry:@Rv:@Ru:@Rw:((%9)<<1|1,%0)
  891. #define @Rv:@Ru:@Rw:(%9,d%0) @Rx:@Ry:@Rv:@Ru:@Rw:((%9)<<1|1,%0)
  892. #define @Ru:@Rw:(%9,f%0) @Rx:@Ry:@Rv:@Ru:@Rw:((%9)<<1|1,%0)
  893. #define @Rw:(%9,) (%9)
  894. /*#define @Ru:(%0i,%1) ~(1<<%1)&@Rx:@Ry:@Rw:@Rv:@Ru:(%0,%1+1)
  895. #define @Rv:@Ru:(%0d,%1) ~(1<<%1)&@Rx:@Ry:@Rw:@Rv:@Ru:(%0,%1+1)
  896. #define @Rw:@Rv:@Ru:(%0f,%1) ~(1<<%1)&@Rx:@Ry:@Rw:@Rv:@Ru:(%0,%1+1)
  897. #define @Rx:@Ry:@Rw:@Rv:@Ru:(%0s,%1) @Rx:@Ry:@Rw:@Rv:@Ru:(%0,%1+1)
  898. #define @Ry:@Rw:@Rv:@Ru:(,%1) ~(1<<%1)*/