impl.inc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  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. #if !defined MAX_NESTED_PASSTHROUGHS
  65. #define MAX_NESTED_PASSTHROUGHS (4) // Should be MORE than enough!
  66. #endif
  67. static stock
  68. YSI_g_sLength[MAX_NESTED_PASSTHROUGHS], // false = O0, true = O1.
  69. YSI_g_sSkips[MAX_NESTED_PASSTHROUGHS],
  70. YSI_g_sStacks[MAX_NESTED_PASSTHROUGHS],
  71. YSI_g_sContexts[MAX_NESTED_PASSTHROUGHS][AsmContext],
  72. YSI_g_sMaxNesting = 0,
  73. YSI_g_sPassthroughNestings = 0,
  74. YSI_g_sInitialised = 0;
  75. #define CALL@YVA2_DoPush YVA2_DoPush(-1, -1, -1)
  76. static stock YVA2_DoPush(const skippedBytes, const pushedBytes, const pushRequirements)
  77. {
  78. // We need to tell the real call how many parameters are being pushed, via
  79. // the hidden 'numargs' parameter. The first obvious solution would be
  80. // code rewriting to change the 'PUSH.C', but that doesn't work with JIT or
  81. // recursion. The next obvious solution is a global with 'PUSH', but that
  82. // again doesn't work with recursion and is tricky with nested calls. The
  83. // only other solution is to create a local variable and use that. This
  84. // means pushing extra data and shifting and modifying code to remove the
  85. // extra data from the stack after the 'PUSH.S' and 'CALL'. This is by far
  86. // the trickiest method, but also the most robust.
  87. //
  88. // Nested calls are still a problem - the outer call must allocate all the
  89. // stack data for all of the parameter counts at once so that they have a
  90. // constant offset from the current frame pointer. This is TODO: I want it
  91. // working for just one outer call first before I start complicating it.
  92. // This also means that the first version will use a global variable and
  93. // ignore recursion and code shifting.
  94. //
  95. // Used in reality, but copied to statics first.
  96. #pragma unused pushedBytes, skippedBytes, pushRequirements
  97. static
  98. sReturnAddress,
  99. sPushedParameters,
  100. sParametersBase,
  101. sTotalParameters;
  102. // Although "pushedBytes" is "const", we actually rewrite the value at
  103. // the call site when the mode starts. By default it is ALWAYS 0, because
  104. // we have no way to determine the true value at compile time. We must
  105. // read it from the number of parameters given to the target function.
  106. // "pushRequirements" is not currently used. It will be the number of bytes
  107. // that each nested function will need to add to the stack for parameter
  108. // count references.
  109. //
  110. // During the execution of this function, we have to actually modify our own
  111. // stack to make room UNDER it for all the parameters being pushed. Then
  112. // when this function is returned from, the bottom of the stack already has
  113. // all the parameters on it. Normally, because this function call is a
  114. // parameter to another function, its return value will also be pushed to
  115. // the stack, but we NOP that out at initialisation time.
  116. //
  117. // Or entry to this function, the stack looks like:
  118. //
  119. // 20 - pushRequirements.
  120. // 16 - pushedBytes.
  121. // 12 - skippedBytes.
  122. // 08 - Parameter count in bytes (8).
  123. // 04 - Return address.
  124. // 00 - Previous frame.
  125. //
  126. // This means there is 24 bytes of information we need to move to globals,
  127. // since we are about to totally destroy our own stack.
  128. #emit POP.pri
  129. #emit SCTRL 5 // Go back to the previous frame.
  130. // The frame is now the same.
  131. #emit POP.pri
  132. #emit MOVE.alt
  133. #emit STOR.pri sReturnAddress
  134. #emit POP.pri // Don't care about this parameter count (don't save).
  135. #emit POP.alt // skippedBytes
  136. #emit LCTRL 5 // frame
  137. #emit ADD.C 12 // frame + 12
  138. #emit ADD // frame + 12 + skippedBytes
  139. #emit STOR.pri sParametersBase
  140. #emit LOAD.S.pri 8 // parameterCount
  141. #emit SUB // parameterCount - skippedBytes
  142. #emit STOR.pri sPushedParameters
  143. #emit POP.alt // pushedBytes
  144. #emit ADD // parameterCount - skippedBytes + pushedBytes
  145. #emit STOR.pri sTotalParameters // (Adjusted).
  146. #emit POP.alt
  147. // The stack is now back to where it was before this function was called.
  148. // So is the frame, so technically now everything is in the same stack.
  149. // We must copy parameters from the previous stack to the current stack.
  150. // There are two methods for this - increase the stack and 'memcpy' in, or
  151. // loop over all the parameters and re-push them.
  152. // Already got the offset - use it.
  153. #emit LOAD.pri sTotalParameters
  154. #emit STOR.I
  155. // Move the stack down enough for the new parameters. `[stk]` is in `pri`.
  156. #emit LCTRL 4
  157. #emit LOAD.alt sPushedParameters
  158. #emit SUB
  159. #emit SCTRL 4 // Move the stack down a load at once.
  160. // The stack pointer should be in `alt`. This code is just:
  161. //
  162. // memcpy(&stack, sParametersBase, 0, sPushedParameters, cellmax);
  163. //
  164. // We know we MUST have enough space on the stack since we allocated it!
  165. #emit STACK 0
  166. #emit PUSH.C 0x10000000
  167. #emit PUSH sPushedParameters
  168. #emit PUSH.C 0
  169. #emit PUSH sParametersBase
  170. #emit PUSH.alt
  171. #emit PUSH.C 20
  172. #emit SYSREQ.C memcpy
  173. #emit STACK 24
  174. // Return from this function. We are already in the parent frame and have
  175. // no parameters on the stack, so function postamble is not required.
  176. #emit LOAD.pri sReturnAddress
  177. #emit SCTRL 8
  178. #emit SCTRL 6
  179. }
  180. // Enable `&YVA2_DummyPush` WITHOUT matching it within the scanner itself.
  181. #define CALL@YVA2_DummyPush YVA2_DummyPush(-1, -1, -1, -1, -1, -1, -1, -1, -1)
  182. // IF YOU EVER CHANGE THE NUMBER OF DUMMY PARAMETERS, DON'T FORGET TO CHANGE THE
  183. // PARAMETER BYTE COUNT IN THE SCANNER! ONLY DONE IT EVERY TIME SO FAR...
  184. stock YVA2_DummyPush(const skippedBytes = 0, const pushedBytes = 0, const pushRequirements = 4, const dummy0 = 0, const dummy1 = 0, const dummy2 = 0, const dummy3 = 0, const dummy4 = 0, const dummy5 = 0)
  185. {
  186. P:3("YVA2_DummyPush start");
  187. #pragma unused dummy0, dummy1, dummy2, dummy3, dummy4, dummy5
  188. // This function serves four purposes:
  189. //
  190. // 1) It is the function called if `___` is used incorrectly, so the code
  191. // will give an error instead of just crashing.
  192. //
  193. // 2) It uses more code space to call than `YVA2_DoPush` does, so adds
  194. // extra space we can inject code in to.
  195. //
  196. // 3) It protects `YVA2_DoPush` from being directly called by users.
  197. //
  198. // 4) It ensures that `YVA2_DoPush` is included in the binary only when
  199. // this function is called. This doesn't save LOADS of space, since
  200. // we always have all the matcher functions - but if they are
  201. // including this library they probably want to use this library!
  202. //
  203. if (YSI_g_sInitialised)
  204. {
  205. P:W("Bare `___` usage found - make sure it is a function parameter.");
  206. }
  207. else if (TRUE)
  208. {
  209. // Save the return address.
  210. #emit LOAD.S.pri 4
  211. #emit STOR.pri YSI_g_sInitialised
  212. // Run lazy initialisation. This now means that the code is only
  213. // scanned and set up when the first `___` is actually found.
  214. YVA2_Initalise();
  215. // Change the return to jump back to the start of this code.
  216. #emit LOAD.pri YSI_g_sInitialised
  217. #emit STOR.S.pri 4
  218. }
  219. else
  220. {
  221. YVA2_DoPush(pushedBytes, skippedBytes, pushRequirements);
  222. }
  223. return 0;
  224. }
  225. public OnScriptInit()
  226. {
  227. // To ensure it is correctly initialised with the JIT as well, since we
  228. // switched to lazy loading. VERY few YSI features can be used in
  229. // `OnCodeInit`, and if there are ever any issues relating to that, the
  230. // correct response is "don't do that"!
  231. if (!YSI_g_sInitialised)
  232. {
  233. YVA2_Initalise();
  234. YSI_g_sInitialised = -1;
  235. }
  236. #if defined VA_OnScriptInit
  237. return VA_OnScriptInit();
  238. #else
  239. return 1;
  240. #endif
  241. }
  242. #undef OnScriptInit
  243. #define OnScriptInit VA_OnScriptInit
  244. #if defined VA_OnScriptInit
  245. forward VA_OnScriptInit();
  246. #endif
  247. static stock YVA2_CodeGenPushSite(ctx[AsmContext], const pushedBytes, const skippedBytes/*, const depth*/, const offset)
  248. {
  249. // `pushedBytes` is the number of bytes originally pushed to the target
  250. // function (which includes four for the `___` return value).
  251. // Use `ctx` so we can use `@emit`.
  252. @emit ADDR.pri offset // 8
  253. @emit PUSH.pri // 12
  254. @emit PUSH.C pushedBytes - 4 // 28
  255. @emit PUSH.C skippedBytes // 20
  256. @emit PUSH.C 12 // 36
  257. @emit CALL.abs addressof (YVA2_DoPush) // 44
  258. // We keep the `HEAP` here since we know that the original code wants to
  259. // clean up an extra four bytes from the heap, so it still needs to exist.
  260. @emit HEAP 4 // 52
  261. }
  262. static stock YVA2_CodeGenShiftCode(dest, src, end)
  263. {
  264. // Shift some the code up. We can't use `memcpy` as the destination most
  265. // probably overlaps the source.
  266. while (src != end)
  267. {
  268. #emit LREF.S.pri src
  269. #emit SREF.S.pri dest
  270. src += 4;
  271. dest += 4;
  272. }
  273. // NOP out the remaining code.
  274. src = _:RelocateOpcode(OP_NOP);
  275. while (dest != end)
  276. {
  277. #emit LOAD.S.pri src
  278. #emit SREF.S.pri dest
  279. dest += 4;
  280. }
  281. }
  282. static stock YVA2_CodeGenMainCleanup(ctx[AsmContext])
  283. {
  284. static
  285. sTemp;
  286. // Get the number of pushed parameters.
  287. @emit POP.alt // 4
  288. // Save the return value.
  289. @emit STOR.pri ref(sTemp) // 12
  290. // Load the stack pointer.
  291. @emit LCTRL 4 // 20
  292. // Remove the number of pushed parameters.
  293. @emit ADD // 24
  294. // Store the stack pointer.
  295. @emit SCTRL 4 // 32
  296. // Restore the return value.
  297. @emit LOAD.pri ref(sTemp) // 40
  298. }
  299. static stock YVA2_CodeGenDeepCleanup(ctx[AsmContext], depth, returningString)
  300. {
  301. if (returningString)
  302. @emit POP.pri
  303. // Remove all the temporary storage locations.
  304. @emit STACK depth * cellbytes
  305. }
  306. static stock YVA2_CodeGenPushVariable(ctx[AsmContext], stack)
  307. {
  308. // Remove a single temporary storage location.
  309. @emit PUSH.S stack
  310. }
  311. static stock YVA2_FoundCall(m[CodeScanner])
  312. {
  313. if (YSI_g_sPassthroughNestings)
  314. {
  315. new
  316. pos = YSI_g_sPassthroughNestings - 1;
  317. if ((CodeScanGetMatchStack(m) < YSI_g_sStacks[pos]) || (!pos && YSI_g_sSkips[0] & cellmin && CodeScanGetMatchStack(m) < YSI_g_sStacks[pos] + cellbytes))
  318. {
  319. new
  320. len = CodeScanGetMatchLength(m),
  321. end = CodeScanGetMatchAddressData(m) + len,
  322. hole = CodeScanGetMatchHole(m, 0),
  323. returningString = 0,
  324. codeLength = 52;
  325. YSI_g_sPassthroughNestings = pos;
  326. if (!pos && YSI_g_sSkips[0] & cellmin)
  327. {
  328. YSI_g_sSkips[0] &= cellmax;
  329. // Check if this function is returning a string. It has an extra hidden
  330. // parameter that we need to account for and that is always pushed
  331. // before our various ones. This is only a problem for an outer call
  332. // since our local parameter count variables will be pushed after that
  333. // return address and hide it. Inner calls don't push their own locals
  334. // and so don't interrupt their own return values.
  335. new
  336. dctx[DisasmContext];
  337. CodeScanGetMatchDisasm(m, dctx, len);
  338. switch (DisasmNextInsn(dctx))
  339. {
  340. // Check the next instruction instead.
  341. case OP_HEAP:
  342. returningString = (DisasmNextInsn(dctx) == OP_POP_PRI) ? 3 * cellbytes : 0;
  343. case OP_POP_PRI:
  344. returningString = 1 * cellbytes;
  345. default:
  346. returningString = 0;
  347. }
  348. if (!returningString)
  349. P:F("Found string return preamble without postamble - Y_Less needs to fix this!");
  350. }
  351. if (returningString)
  352. {
  353. // Shift the three cells before this code down to make room for
  354. // the local storage stack adjustment code.
  355. YSI_g_sContexts[0][AsmContext_buffer] -= 3 * cellbytes,
  356. codeLength = ReadAmxMemory(YSI_g_sContexts[0][AsmContext_buffer] + cellbytes),
  357. AsmEmitStack(YSI_g_sContexts[0], (YSI_g_sMaxNesting + 1) * -cellbytes),
  358. AsmEmitHeap(YSI_g_sContexts[0], codeLength),
  359. AsmEmitPushAlt(YSI_g_sContexts[0]),
  360. codeLength = 52 + 5 * cellbytes;
  361. }
  362. else if (!pos)
  363. {
  364. AsmEmitStack(YSI_g_sContexts[0], (YSI_g_sMaxNesting + 1) * -cellbytes),
  365. codeLength += 2 * cellbytes;
  366. }
  367. new
  368. dest = YSI_g_sContexts[pos][AsmContext_buffer];
  369. // First, rewrite the `___` call-site to call `YVA2_DoPush` instead.
  370. YVA2_CodeGenPushSite(YSI_g_sContexts[pos], hole, YSI_g_sSkips[pos], -YSI_g_sStacks[0] - pos * cellbytes), // YSI_g_sStacks[0]!!!
  371. // Shift the code up.
  372. YVA2_CodeGenShiftCode(
  373. dest + YSI_g_sContexts[pos][AsmContext_buffer_offset],
  374. dest + YSI_g_sLength[pos] + (returningString ? 12 : 0),
  375. end + returningString),
  376. // Adjust the assembly context. `codeLength` is the length of the
  377. // code from `YVA2_CodeGenPushSite`.
  378. YSI_g_sContexts[pos][AsmContext_buffer_offset] = (end - dest) - (YSI_g_sLength[pos] - codeLength + returningString) - 16;
  379. if (len == 24)
  380. YSI_g_sContexts[pos][AsmContext_buffer_offset] -= 8;
  381. YVA2_CodeGenPushVariable(YSI_g_sContexts[pos], -YSI_g_sStacks[0] - pos * cellbytes);
  382. YSI_g_sContexts[pos][AsmContext_buffer_offset] += 8;
  383. if (returningString == 12)
  384. YSI_g_sContexts[pos][AsmContext_buffer_offset] += 8;
  385. if (len == 24)
  386. YVA2_CodeGenMainCleanup(YSI_g_sContexts[pos]);
  387. if (YSI_g_sPassthroughNestings)
  388. {
  389. // Is a nesting. Do nothing.
  390. }
  391. else //if (YSI_g_sMaxNesting)
  392. {
  393. // Was nested - needs multiple call's cleanups.
  394. YVA2_CodeGenDeepCleanup(YSI_g_sContexts[pos], (YSI_g_sMaxNesting + 1), returningString),
  395. YSI_g_sMaxNesting = 0;
  396. }
  397. // Just keep going - this code is designed to recurse (maybe...).
  398. return 0;
  399. }
  400. }
  401. return -1;
  402. }
  403. static stock YVA2_FoundPush(m[CodeScanner])
  404. {
  405. new
  406. addr = CodeScanGetMatchAddress(m);
  407. if (YSI_g_sPassthroughNestings >= MAX_NESTED_PASSTHROUGHS)
  408. {
  409. P:F("`___` nested too deeply - increase `MAX_NESTED_PASSTHROUGHS`.");
  410. return;
  411. }
  412. YSI_g_sMaxNesting = max(YSI_g_sMaxNesting, YSI_g_sPassthroughNestings),
  413. // Do something with the found address (of the START of the match), and the
  414. // stack size (of the END of the match) - different for reasons...
  415. YSI_g_sLength[YSI_g_sPassthroughNestings] = CodeScanGetMatchLength(m),
  416. CodeScanGetMatchAsm(m, YSI_g_sContexts[YSI_g_sPassthroughNestings]),
  417. YSI_g_sStacks[YSI_g_sPassthroughNestings] = CodeScanGetMatchStack(m),
  418. // If the code is 96 bytes long, `ZERO_PRI` was used not a constant number.
  419. YSI_g_sSkips[YSI_g_sPassthroughNestings] = (YSI_g_sLength[YSI_g_sPassthroughNestings] == 112) ? 0 : CodeScanGetMatchHole(m, 0);
  420. if (!YSI_g_sPassthroughNestings)
  421. {
  422. // Check if this function is returning a string. It has an extra hidden
  423. // parameter that we need to account for and that is always pushed
  424. // before our various ones. This is only a problem for an outer call
  425. // since our local parameter count variables will be pushed after that
  426. // return address and hide it. Inner calls don't push their own locals
  427. // and so don't interrupt their own return values.
  428. new
  429. dctx[DisasmContext];
  430. CodeScanGetMatchDisasm(m, dctx, -12);
  431. if (DisasmNextInsn(dctx) == OP_HEAP && DisasmNextInsn(dctx) == OP_PUSH_ALT)
  432. {
  433. // Correct preamble. Mark this to check the postamble.
  434. YSI_g_sSkips[YSI_g_sPassthroughNestings] |= cellmin,
  435. YSI_g_sStacks[0] -= cellbytes;
  436. }
  437. }
  438. ++YSI_g_sPassthroughNestings;
  439. P:3("YVA2_FoundPush: %d", CodeScanGetMatchStack(m));
  440. P:5("YVA2_FoundPush: %d %d", addr, YSI_g_sInitialised);
  441. if (addr < YSI_g_sInitialised < addr + 100)
  442. {
  443. P:5("YVA2_FoundPush: Found match");
  444. YSI_g_sInitialised = addr;
  445. }
  446. }
  447. // Add a scanner to find the `___` function call.
  448. // Add a scanner to find the next point at which the stack is smaller than it
  449. // was when `___` was called. We have to be careful here as there may have been
  450. // another `___` call in the interim, which would have been fully resolved
  451. // first. As in:
  452. //
  453. // Func1(Func2(___), ___);
  454. //
  455. // `Func1`'s `___` will be first in the code, but before finding the call to
  456. // `Func1` itself, we would see the inner `___` AND the inner function call.
  457. static stock YVA2_Initalise()
  458. {
  459. P:3("YVA2_Initalise start");
  460. new
  461. scanner[CodeScanner],
  462. csmO0A[CodeScanMatcher],
  463. csmO0B[CodeScanMatcher],
  464. csmO1[CodeScanMatcher],
  465. csm2[CodeScanMatcher],
  466. csm3[CodeScanMatcher];
  467. CodeScanInit(scanner);
  468. /*
  469. // O0:
  470. const.pri 4
  471. push.pri
  472. zero.pri
  473. push.pri
  474. zero.pri
  475. push.pri
  476. push.c c
  477. call YVA2_DoPush
  478. heap 4
  479. stor.i
  480. move.pri
  481. push.pri
  482. */
  483. CodeScanMatcherInit(csmO0A, &YVA2_FoundPush);
  484. CodeScanMatcherPattern(csmO0A,
  485. OP(ZERO_PRI) // 4
  486. OP(PUSH_PRI) // 8
  487. OP(ZERO_PRI) // 12
  488. OP(PUSH_PRI) // 16
  489. OP(ZERO_PRI) // 20
  490. OP(PUSH_PRI) // 24
  491. OP(ZERO_PRI) // 28
  492. OP(PUSH_PRI) // 32
  493. OP(ZERO_PRI) // 36
  494. OP(PUSH_PRI) // 40
  495. OP(ZERO_PRI) // 44
  496. OP(PUSH_PRI) // 48
  497. OP(CONST_PRI, 4) // 56
  498. OP(PUSH_PRI) // 60
  499. OP(ZERO_PRI) // 64
  500. OP(PUSH_PRI) // 68
  501. OP(ZERO_PRI) // 72
  502. OP(PUSH_PRI) // 76
  503. OP(PUSH_C, 36) // 84
  504. OP(CALL, &YVA2_DummyPush) // 92
  505. OP(HEAP, 4) // 100
  506. OP(STOR_I) // 104
  507. OP(MOVE_PRI) // 108
  508. OP(PUSH_PRI) // 112
  509. );
  510. CodeScanAddMatcher(scanner, csmO0A);
  511. CodeScanMatcherInit(csmO0B, &YVA2_FoundPush);
  512. CodeScanMatcherPattern(csmO0B,
  513. OP(ZERO_PRI) // 4
  514. OP(PUSH_PRI) // 8
  515. OP(ZERO_PRI) // 12
  516. OP(PUSH_PRI) // 16
  517. OP(ZERO_PRI) // 20
  518. OP(PUSH_PRI) // 24
  519. OP(ZERO_PRI) // 28
  520. OP(PUSH_PRI) // 32
  521. OP(ZERO_PRI) // 36
  522. OP(PUSH_PRI) // 40
  523. OP(ZERO_PRI) // 44
  524. OP(PUSH_PRI) // 48
  525. OP(CONST_PRI, 4) // 56
  526. OP(PUSH_PRI) // 60
  527. OP(ZERO_PRI) // 64
  528. OP(PUSH_PRI) // 68
  529. OP(CONST_PRI, ???) // 76
  530. OP(PUSH_PRI) // 80
  531. OP(PUSH_C, 36) // 88
  532. OP(CALL, &YVA2_DummyPush) // 96
  533. OP(HEAP, 4) // 104
  534. OP(STOR_I) // 108
  535. OP(MOVE_PRI) // 112
  536. OP(PUSH_PRI) // 116
  537. );
  538. CodeScanAddMatcher(scanner, csmO0B);
  539. /*
  540. // O1:
  541. push.c 4
  542. push.c 0
  543. push.c 0
  544. push.c c
  545. call YVA2_DoPush
  546. heap 4
  547. stor.i
  548. push.alt
  549. */
  550. CodeScanMatcherInit(csmO1, &YVA2_FoundPush);
  551. CodeScanMatcherPattern(csmO1,
  552. OP(PUSH_C, 0) // 8
  553. OP(PUSH_C, 0) // 16
  554. OP(PUSH_C, 0) // 24
  555. OP(PUSH_C, 0) // 32
  556. OP(PUSH_C, 0) // 40
  557. OP(PUSH_C, 0) // 48
  558. OP(PUSH_C, 4) // 56
  559. OP(PUSH_C, 0) // 64
  560. OP(PUSH_C, ???) // 72
  561. OP(PUSH_C, 36) // 80
  562. OP(CALL, &YVA2_DummyPush) // 84
  563. OP(HEAP, 4) // 92
  564. OP(STOR_I) // 96
  565. OP(PUSH_ALT) // 100
  566. );
  567. CodeScanAddMatcher(scanner, csmO1);
  568. // Match ANY function calls anywhere. Will even match calls to `___`, but
  569. // we can ignore them (and many others). I need some way to determine that
  570. // "amx_assembly" is up-to-date with the very latest "codescan" changes to
  571. // add scanner ignoring.
  572. /*
  573. push.c ???
  574. call ???
  575. stack ???
  576. */
  577. CodeScanMatcherInit(csm2, &YVA2_FoundCall);
  578. CodeScanMatcherPattern(csm2,
  579. OP(PUSH_C, ???)
  580. OP(CALL, ???)
  581. );
  582. CodeScanAddMatcher(scanner, csm2);
  583. /*
  584. push.c ???
  585. sysreq.c ???
  586. stack ???
  587. */
  588. CodeScanMatcherInit(csm3, &YVA2_FoundCall);
  589. CodeScanMatcherPattern(csm3,
  590. OP(PUSH_C, ???)
  591. OP(SYSREQ_C, ???)
  592. OP(STACK, ???)
  593. );
  594. CodeScanAddMatcher(scanner, csm3);
  595. // Replace calls with the correct parameter counts etc.
  596. CodeScanRun(scanner);
  597. //DisasmDump("yva2.asm");
  598. return 1;
  599. }
  600. stock va_strlen(arg)
  601. {
  602. // Get the length of the string at the given position on the previous
  603. // function's stack (convenience function).
  604. // Get the address of the previous function's stack. First get the index of
  605. // the argument required.
  606. #emit LOAD.S.pri arg
  607. // Then convert that number to bytes from cells.
  608. #emit SMUL.C 4
  609. // Get the previous function's frame. Stored in variable 0 (in the current
  610. // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is
  611. // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add
  612. // checks that "arg * 4 < *(*(FRM + 0) + 8)", for the previous frame parameter
  613. // count (in C pointer speak).
  614. #emit LOAD.S.alt 0
  615. // Add the frame pointer to the argument offset in bytes.
  616. #emit ADD
  617. // Add 12 to skip over the function header.
  618. #emit ADD.C 12
  619. // Load the address stored in the specified address.
  620. #emit LOAD.I
  621. // Push the address we just determined was the source.
  622. #emit PUSH.pri
  623. // Push the number of parameters passed (in bytes) to the function.
  624. #emit PUSH.C 4
  625. // Call the function.
  626. #emit SYSREQ.C strlen
  627. // Restore the stack to its level before we called this native.
  628. #emit STACK 8
  629. #emit RETN
  630. // Never called.
  631. return 0;
  632. }
  633. stock va_getstring(dest[], arg, len = sizeof (dest))
  634. {
  635. // Get the address of the previous function's stack. First get the index of
  636. // the argument required.
  637. #emit LOAD.S.pri arg
  638. // Then convert that number to bytes from cells.
  639. #emit SMUL.C 4
  640. // Get the previous function's frame. Stored in variable 0 (in the current
  641. // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is
  642. // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add
  643. // checks that "arg * 4 < *(*(FRM + 0) + 8)", for the previous frame parameter
  644. // count (in C pointer speak).
  645. #emit LOAD.S.alt 0
  646. // Add the frame pointer to the argument offset in bytes.
  647. #emit ADD
  648. // Add 12 to skip over the function header.
  649. #emit ADD.C 12
  650. // Load the address stored in the specified address.
  651. #emit LOAD.I
  652. // Push the length for "strcat".
  653. #emit PUSH.S len
  654. // Push the address we just determined was the source.
  655. #emit PUSH.pri
  656. // Load the address of the destination.
  657. #emit LOAD.S.alt dest
  658. // Blank the first cell so "strcat" behaves like "strcpy".
  659. #emit ZERO.pri
  660. // Store the loaded number 0 to the loaded address.
  661. #emit STOR.I
  662. // Push the loaded address.
  663. #emit PUSH.alt
  664. // Push the number of parameters passed (in bytes) to the function.
  665. #emit PUSH.C 12
  666. // Call the function.
  667. #emit SYSREQ.C strcat
  668. // Restore the stack to its level before we called this native.
  669. #emit STACK 16
  670. }
  671. stock PlayerText:va_CreatePlayerTextDraw(playerid, Float:x, Float:y, fmat[], va_args<>)
  672. {
  673. return CreatePlayerTextDraw(playerid, x, y, va_return(fmat, va_start<4>));
  674. }
  675. stock Text:va_TextDrawCreate(Float:x, Float:y, fmat[], va_args<>)
  676. {
  677. return TextDrawCreate(x, y, va_return(fmat, va_start<3>));
  678. }
  679. stock va_SendClientMessage(playerid, colour, const fmat[], va_args<>)
  680. {
  681. return SendClientMessage(playerid, colour, va_return(fmat, va_start<3>));
  682. }
  683. stock va_SendClientMessageToAll(colour, const fmat[], va_args<>)
  684. {
  685. return SendClientMessageToAll(colour, va_return(fmat, va_start<2>));
  686. }
  687. stock va_SendPlayerMessageToPlayer(playerid, senderid, const fmat[], va_args<>)
  688. {
  689. return SendPlayerMessageToPlayer(playerid, senderid, va_return(fmat, va_start<3>));
  690. }
  691. stock va_SendPlayerMessageToAll(senderid, const fmat[], va_args<>)
  692. {
  693. return SendPlayerMessageToAll(senderid, va_return(fmat, va_start<2>));
  694. }
  695. stock va_GameTextForPlayer(playerid, const fmat[], time, style, va_args<>)
  696. {
  697. return GameTextForPlayer(playerid, va_return(fmat, va_start<4>), time, style);
  698. }
  699. stock va_GameTextForAll(const fmat[], time, style, va_args<>)
  700. {
  701. return GameTextForAll(va_return(fmat, va_start<3>), time, style);
  702. }
  703. stock va_print(const fmat[], va_args<>)
  704. {
  705. return print(va_return(fmat, va_start<1>));
  706. }
  707. stock va_fprintf(File:fhnd, const fmat[], va_args<>)
  708. {
  709. return fwrite(fhnd, va_return(fmat, va_start<2>));
  710. }