y_va_impl.inc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  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 OnCodeInit()
  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_OnCodeInit
  237. VA_OnCodeInit();
  238. #endif
  239. return 1;
  240. }
  241. #undef OnCodeInit
  242. #define OnCodeInit VA_OnCodeInit
  243. #if defined VA_OnCodeInit
  244. forward VA_OnCodeInit();
  245. #endif
  246. static stock YVA2_CodeGenPushSite(ctx[AsmContext], const pushedBytes, const skippedBytes/*, const depth*/, const offset)
  247. {
  248. // `pushedBytes` is the number of bytes originally pushed to the target
  249. // function (which includes four for the `___` return value).
  250. // Use `ctx` so we can use `@emit`.
  251. @emit ADDR.pri offset // 8
  252. @emit PUSH.pri // 12
  253. @emit PUSH.C pushedBytes - 4 // 28
  254. @emit PUSH.C skippedBytes // 20
  255. @emit PUSH.C 12 // 36
  256. @emit CALL.abs addressof (YVA2_DoPush) // 44
  257. // We keep the `HEAP` here since we know that the original code wants to
  258. // clean up an extra four bytes from the heap, so it still needs to exist.
  259. @emit HEAP 4 // 52
  260. }
  261. static stock YVA2_CodeGenShiftCode(dest, src, end)
  262. {
  263. // Shift some the code up. We can't use `memcpy` as the destination most
  264. // probably overlaps the source.
  265. while (src != end)
  266. {
  267. #emit LREF.S.pri src
  268. #emit SREF.S.pri dest
  269. src += 4;
  270. dest += 4;
  271. }
  272. // NOP out the remaining code.
  273. src = _:RelocateOpcode(OP_NOP);
  274. while (dest != end)
  275. {
  276. #emit LOAD.S.pri src
  277. #emit SREF.S.pri dest
  278. dest += 4;
  279. }
  280. }
  281. static stock YVA2_CodeGenMainCleanup(ctx[AsmContext])
  282. {
  283. static
  284. sTemp;
  285. // Get the number of pushed parameters.
  286. @emit POP.alt // 4
  287. // Save the return value.
  288. @emit STOR.pri ref(sTemp) // 12
  289. // Load the stack pointer.
  290. @emit LCTRL 4 // 20
  291. // Remove the number of pushed parameters.
  292. @emit ADD // 24
  293. // Store the stack pointer.
  294. @emit SCTRL 4 // 32
  295. // Restore the return value.
  296. @emit LOAD.pri ref(sTemp) // 40
  297. }
  298. static stock YVA2_CodeGenDeepCleanup(ctx[AsmContext], depth, returningString)
  299. {
  300. if (returningString)
  301. @emit POP.pri
  302. // Remove all the temporary storage locations.
  303. @emit STACK depth * cellbytes
  304. }
  305. static stock YVA2_CodeGenPushVariable(ctx[AsmContext], stack)
  306. {
  307. // Remove a single temporary storage location.
  308. @emit PUSH.S stack
  309. }
  310. static stock YVA2_FoundCall(const m[CodeScanner])
  311. {
  312. if (YSI_g_sPassthroughNestings)
  313. {
  314. new
  315. pos = YSI_g_sPassthroughNestings - 1;
  316. if ((CodeScanGetMatchStack(m) < YSI_g_sStacks[pos]) || (!pos && YSI_g_sSkips[0] & cellmin && CodeScanGetMatchStack(m) < YSI_g_sStacks[pos] + cellbytes))
  317. {
  318. new
  319. len = CodeScanGetMatchLength(m),
  320. end = CodeScanGetMatchAddressData(m) + len,
  321. hole = CodeScanGetMatchHole(m, 0),
  322. returningString = 0,
  323. codeLength = 52;
  324. YSI_g_sPassthroughNestings = pos;
  325. if (!pos && YSI_g_sSkips[0] & cellmin)
  326. {
  327. YSI_g_sSkips[0] &= cellmax;
  328. // Check if this function is returning a string. It has an extra hidden
  329. // parameter that we need to account for and that is always pushed
  330. // before our various ones. This is only a problem for an outer call
  331. // since our local parameter count variables will be pushed after that
  332. // return address and hide it. Inner calls don't push their own locals
  333. // and so don't interrupt their own return values.
  334. new
  335. dctx[DisasmContext];
  336. CodeScanGetMatchDisasm(m, dctx, len);
  337. switch (DisasmNextInsn(dctx))
  338. {
  339. // Check the next instruction instead.
  340. case OP_HEAP:
  341. returningString = (DisasmNextInsn(dctx) == OP_POP_PRI) ? 3 * cellbytes : 0;
  342. case OP_POP_PRI:
  343. returningString = 1 * cellbytes;
  344. default:
  345. returningString = 0;
  346. }
  347. if (!returningString)
  348. P:F("Found string return preamble without postamble - Y_Less needs to fix this!");
  349. }
  350. if (returningString)
  351. {
  352. // Shift the three cells before this code down to make room for
  353. // the local storage stack adjustment code.
  354. YSI_g_sContexts[0][AsmContext_buffer] -= 3 * cellbytes,
  355. codeLength = ReadAmxMemory(YSI_g_sContexts[0][AsmContext_buffer] + cellbytes),
  356. AsmEmitStack(YSI_g_sContexts[0], (YSI_g_sMaxNesting + 1) * -cellbytes),
  357. AsmEmitHeap(YSI_g_sContexts[0], codeLength),
  358. AsmEmitPushAlt(YSI_g_sContexts[0]),
  359. codeLength = 52 + 5 * cellbytes;
  360. }
  361. else if (!pos)
  362. {
  363. AsmEmitStack(YSI_g_sContexts[0], (YSI_g_sMaxNesting + 1) * -cellbytes),
  364. codeLength += 2 * cellbytes;
  365. }
  366. new
  367. dest = YSI_g_sContexts[pos][AsmContext_buffer];
  368. // First, rewrite the `___` call-site to call `YVA2_DoPush` instead.
  369. YVA2_CodeGenPushSite(YSI_g_sContexts[pos], hole, YSI_g_sSkips[pos], -YSI_g_sStacks[0] - pos * cellbytes), // YSI_g_sStacks[0]!!!
  370. // Shift the code up.
  371. YVA2_CodeGenShiftCode(
  372. dest + YSI_g_sContexts[pos][AsmContext_buffer_offset],
  373. dest + YSI_g_sLength[pos] + (returningString ? 12 : 0),
  374. end + returningString),
  375. // Adjust the assembly context. `codeLength` is the length of the
  376. // code from `YVA2_CodeGenPushSite`.
  377. YSI_g_sContexts[pos][AsmContext_buffer_offset] = (end - dest) - (YSI_g_sLength[pos] - codeLength + returningString) - 16;
  378. if (len == 24)
  379. YSI_g_sContexts[pos][AsmContext_buffer_offset] -= 8;
  380. YVA2_CodeGenPushVariable(YSI_g_sContexts[pos], -YSI_g_sStacks[0] - pos * cellbytes);
  381. YSI_g_sContexts[pos][AsmContext_buffer_offset] += 8;
  382. if (returningString == 12)
  383. YSI_g_sContexts[pos][AsmContext_buffer_offset] += 8;
  384. if (len == 24)
  385. YVA2_CodeGenMainCleanup(YSI_g_sContexts[pos]);
  386. if (YSI_g_sPassthroughNestings)
  387. {
  388. // Is a nesting. Do nothing.
  389. }
  390. else //if (YSI_g_sMaxNesting)
  391. {
  392. // Was nested - needs multiple call's cleanups.
  393. YVA2_CodeGenDeepCleanup(YSI_g_sContexts[pos], (YSI_g_sMaxNesting + 1), returningString),
  394. YSI_g_sMaxNesting = 0;
  395. }
  396. // Just keep going - this code is designed to recurse (maybe...).
  397. return 0;
  398. }
  399. }
  400. return -1;
  401. }
  402. static stock YVA2_FoundPush(const m[CodeScanner])
  403. {
  404. new
  405. addr = CodeScanGetMatchAddress(m);
  406. if (YSI_g_sPassthroughNestings >= MAX_NESTED_PASSTHROUGHS)
  407. {
  408. P:F("`___` nested too deeply - increase `MAX_NESTED_PASSTHROUGHS`.");
  409. return;
  410. }
  411. YSI_g_sMaxNesting = max(YSI_g_sMaxNesting, YSI_g_sPassthroughNestings),
  412. // Do something with the found address (of the START of the match), and the
  413. // stack size (of the END of the match) - different for reasons...
  414. YSI_g_sLength[YSI_g_sPassthroughNestings] = CodeScanGetMatchLength(m),
  415. CodeScanGetMatchAsm(m, YSI_g_sContexts[YSI_g_sPassthroughNestings]),
  416. YSI_g_sStacks[YSI_g_sPassthroughNestings] = CodeScanGetMatchStack(m),
  417. // If the code is 96 bytes long, `ZERO_PRI` was used not a constant number.
  418. YSI_g_sSkips[YSI_g_sPassthroughNestings] = (YSI_g_sLength[YSI_g_sPassthroughNestings] == 112) ? 0 : CodeScanGetMatchHole(m, 0);
  419. if (!YSI_g_sPassthroughNestings)
  420. {
  421. // Check if this function is returning a string. It has an extra hidden
  422. // parameter that we need to account for and that is always pushed
  423. // before our various ones. This is only a problem for an outer call
  424. // since our local parameter count variables will be pushed after that
  425. // return address and hide it. Inner calls don't push their own locals
  426. // and so don't interrupt their own return values.
  427. new
  428. dctx[DisasmContext];
  429. CodeScanGetMatchDisasm(m, dctx, -12);
  430. if (DisasmNextInsn(dctx) == OP_HEAP && DisasmNextInsn(dctx) == OP_PUSH_ALT)
  431. {
  432. // Correct preamble. Mark this to check the postamble.
  433. YSI_g_sSkips[YSI_g_sPassthroughNestings] |= cellmin,
  434. YSI_g_sStacks[0] -= cellbytes;
  435. }
  436. }
  437. ++YSI_g_sPassthroughNestings;
  438. P:3("YVA2_FoundPush: %d", CodeScanGetMatchStack(m));
  439. P:5("YVA2_FoundPush: %d %d", addr, YSI_g_sInitialised);
  440. if (addr < YSI_g_sInitialised < addr + 100)
  441. {
  442. P:5("YVA2_FoundPush: Found match");
  443. YSI_g_sInitialised = addr;
  444. }
  445. }
  446. // Add a scanner to find the `___` function call.
  447. // Add a scanner to find the next point at which the stack is smaller than it
  448. // was when `___` was called. We have to be careful here as there may have been
  449. // another `___` call in the interim, which would have been fully resolved
  450. // first. As in:
  451. //
  452. // Func1(Func2(___), ___);
  453. //
  454. // `Func1`'s `___` will be first in the code, but before finding the call to
  455. // `Func1` itself, we would see the inner `___` AND the inner function call.
  456. static stock YVA2_Initalise()
  457. {
  458. P:3("YVA2_Initalise start");
  459. new
  460. scanner[CodeScanner],
  461. csmO0A[CodeScanMatcher],
  462. csmO0B[CodeScanMatcher],
  463. csmO1[CodeScanMatcher],
  464. csm2[CodeScanMatcher],
  465. csm3[CodeScanMatcher];
  466. CodeScanInit(scanner);
  467. /*
  468. // O0:
  469. const.pri 4
  470. push.pri
  471. zero.pri
  472. push.pri
  473. zero.pri
  474. push.pri
  475. push.c c
  476. call YVA2_DoPush
  477. heap 4
  478. stor.i
  479. move.pri
  480. push.pri
  481. */
  482. CodeScanMatcherInit(csmO0A, &YVA2_FoundPush);
  483. CodeScanMatcherPattern(csmO0A,
  484. OP(ZERO_PRI) // 4
  485. OP(PUSH_PRI) // 8
  486. OP(ZERO_PRI) // 12
  487. OP(PUSH_PRI) // 16
  488. OP(ZERO_PRI) // 20
  489. OP(PUSH_PRI) // 24
  490. OP(ZERO_PRI) // 28
  491. OP(PUSH_PRI) // 32
  492. OP(ZERO_PRI) // 36
  493. OP(PUSH_PRI) // 40
  494. OP(ZERO_PRI) // 44
  495. OP(PUSH_PRI) // 48
  496. OP(CONST_PRI, 4) // 56
  497. OP(PUSH_PRI) // 60
  498. OP(ZERO_PRI) // 64
  499. OP(PUSH_PRI) // 68
  500. OP(ZERO_PRI) // 72
  501. OP(PUSH_PRI) // 76
  502. OP(PUSH_C, 36) // 84
  503. OP(CALL, &YVA2_DummyPush) // 92
  504. OP(HEAP, 4) // 100
  505. OP(STOR_I) // 104
  506. OP(MOVE_PRI) // 108
  507. OP(PUSH_PRI) // 112
  508. );
  509. CodeScanAddMatcher(scanner, csmO0A);
  510. CodeScanMatcherInit(csmO0B, &YVA2_FoundPush);
  511. CodeScanMatcherPattern(csmO0B,
  512. OP(ZERO_PRI) // 4
  513. OP(PUSH_PRI) // 8
  514. OP(ZERO_PRI) // 12
  515. OP(PUSH_PRI) // 16
  516. OP(ZERO_PRI) // 20
  517. OP(PUSH_PRI) // 24
  518. OP(ZERO_PRI) // 28
  519. OP(PUSH_PRI) // 32
  520. OP(ZERO_PRI) // 36
  521. OP(PUSH_PRI) // 40
  522. OP(ZERO_PRI) // 44
  523. OP(PUSH_PRI) // 48
  524. OP(CONST_PRI, 4) // 56
  525. OP(PUSH_PRI) // 60
  526. OP(ZERO_PRI) // 64
  527. OP(PUSH_PRI) // 68
  528. OP(CONST_PRI, ???) // 76
  529. OP(PUSH_PRI) // 80
  530. OP(PUSH_C, 36) // 88
  531. OP(CALL, &YVA2_DummyPush) // 96
  532. OP(HEAP, 4) // 104
  533. OP(STOR_I) // 108
  534. OP(MOVE_PRI) // 112
  535. OP(PUSH_PRI) // 116
  536. );
  537. CodeScanAddMatcher(scanner, csmO0B);
  538. /*
  539. // O1:
  540. push.c 4
  541. push.c 0
  542. push.c 0
  543. push.c c
  544. call YVA2_DoPush
  545. heap 4
  546. stor.i
  547. push.alt
  548. */
  549. CodeScanMatcherInit(csmO1, &YVA2_FoundPush);
  550. CodeScanMatcherPattern(csmO1,
  551. OP(PUSH_C, 0) // 8
  552. OP(PUSH_C, 0) // 16
  553. OP(PUSH_C, 0) // 24
  554. OP(PUSH_C, 0) // 32
  555. OP(PUSH_C, 0) // 40
  556. OP(PUSH_C, 0) // 48
  557. OP(PUSH_C, 4) // 56
  558. OP(PUSH_C, 0) // 64
  559. OP(PUSH_C, ???) // 72
  560. OP(PUSH_C, 36) // 80
  561. OP(CALL, &YVA2_DummyPush) // 84
  562. OP(HEAP, 4) // 92
  563. OP(STOR_I) // 96
  564. OP(PUSH_ALT) // 100
  565. );
  566. CodeScanAddMatcher(scanner, csmO1);
  567. // Match ANY function calls anywhere. Will even match calls to `___`, but
  568. // we can ignore them (and many others). I need some way to determine that
  569. // "amx_assembly" is up-to-date with the very latest "codescan" changes to
  570. // add scanner ignoring.
  571. /*
  572. push.c ???
  573. call ???
  574. stack ???
  575. */
  576. CodeScanMatcherInit(csm2, &YVA2_FoundCall);
  577. CodeScanMatcherPattern(csm2,
  578. OP(PUSH_C, ???)
  579. OP(CALL, ???)
  580. );
  581. CodeScanAddMatcher(scanner, csm2);
  582. /*
  583. push.c ???
  584. sysreq.c ???
  585. stack ???
  586. */
  587. CodeScanMatcherInit(csm3, &YVA2_FoundCall);
  588. CodeScanMatcherPattern(csm3,
  589. OP(PUSH_C, ???)
  590. OP(SYSREQ_C, ???)
  591. OP(STACK, ???)
  592. );
  593. CodeScanAddMatcher(scanner, csm3);
  594. // Replace calls with the correct parameter counts etc.
  595. CodeScanRunFast(scanner, &YVA2_DummyPush);
  596. //DisasmDump("yva2.asm");
  597. return 1;
  598. }
  599. stock va_strlen(arg)
  600. {
  601. // Get the length of the string at the given position on the previous
  602. // function's stack (convenience function).
  603. // Get the address of the previous function's stack. First get the index of
  604. // the argument required.
  605. #emit LOAD.S.pri arg
  606. // Then convert that number to bytes from cells.
  607. #emit SMUL.C 4
  608. // Get the previous function's frame. Stored in variable 0 (in the current
  609. // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is
  610. // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add
  611. // checks that "arg * 4 < *(*(FRM + 0) + 8)", for the previous frame parameter
  612. // count (in C pointer speak).
  613. #emit LOAD.S.alt 0
  614. // Add the frame pointer to the argument offset in bytes.
  615. #emit ADD
  616. // Add 12 to skip over the function header.
  617. #emit ADD.C 12
  618. // Load the address stored in the specified address.
  619. #emit LOAD.I
  620. // Push the address we just determined was the source.
  621. #emit PUSH.pri
  622. // Push the number of parameters passed (in bytes) to the function.
  623. #emit PUSH.C 4
  624. // Call the function.
  625. #emit SYSREQ.C strlen
  626. // Restore the stack to its level before we called this native.
  627. #emit STACK 8
  628. #emit RETN
  629. // Never called.
  630. return 0;
  631. }
  632. stock va_getstring(dest[], arg, len = sizeof (dest))
  633. {
  634. // Get the address of the previous function's stack. First get the index of
  635. // the argument required.
  636. #emit LOAD.S.pri arg
  637. // Then convert that number to bytes from cells.
  638. #emit SMUL.C 4
  639. // Get the previous function's frame. Stored in variable 0 (in the current
  640. // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is
  641. // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add
  642. // checks that "arg * 4 < *(*(FRM + 0) + 8)", for the previous frame parameter
  643. // count (in C pointer speak).
  644. #emit LOAD.S.alt 0
  645. // Add the frame pointer to the argument offset in bytes.
  646. #emit ADD
  647. // Add 12 to skip over the function header.
  648. #emit ADD.C 12
  649. // Load the address stored in the specified address.
  650. #emit LOAD.I
  651. // Push the length for "strcat".
  652. #emit PUSH.S len
  653. // Push the address we just determined was the source.
  654. #emit PUSH.pri
  655. // Load the address of the destination.
  656. #emit LOAD.S.alt dest
  657. // Blank the first cell so "strcat" behaves like "strcpy".
  658. #emit ZERO.pri
  659. // Store the loaded number 0 to the loaded address.
  660. #emit STOR.I
  661. // Push the loaded address.
  662. #emit PUSH.alt
  663. // Push the number of parameters passed (in bytes) to the function.
  664. #emit PUSH.C 12
  665. // Call the function.
  666. #emit SYSREQ.C strcat
  667. // Restore the stack to its level before we called this native.
  668. #emit STACK 16
  669. }
  670. stock PlayerText:va_CreatePlayerTextDraw(playerid, Float:x, Float:y, const fmat[], va_args<>)
  671. {
  672. return CreatePlayerTextDraw(playerid, x, y, va_return(fmat, va_start<4>));
  673. }
  674. stock Text:va_TextDrawCreate(Float:x, Float:y, const fmat[], va_args<>)
  675. {
  676. return TextDrawCreate(x, y, va_return(fmat, va_start<3>));
  677. }
  678. stock va_SendClientMessage(playerid, colour, const fmat[], va_args<>)
  679. {
  680. return SendClientMessage(playerid, colour, va_return(fmat, va_start<3>));
  681. }
  682. stock va_SendClientMessageToAll(colour, const fmat[], va_args<>)
  683. {
  684. return SendClientMessageToAll(colour, va_return(fmat, va_start<2>));
  685. }
  686. stock va_SendPlayerMessageToPlayer(playerid, senderid, const fmat[], va_args<>)
  687. {
  688. return SendPlayerMessageToPlayer(playerid, senderid, va_return(fmat, va_start<3>));
  689. }
  690. stock va_SendPlayerMessageToAll(senderid, const fmat[], va_args<>)
  691. {
  692. return SendPlayerMessageToAll(senderid, va_return(fmat, va_start<2>));
  693. }
  694. stock va_GameTextForPlayer(playerid, const fmat[], time, style, va_args<>)
  695. {
  696. return GameTextForPlayer(playerid, va_return(fmat, va_start<4>), time, style);
  697. }
  698. stock va_GameTextForAll(const fmat[], time, style, va_args<>)
  699. {
  700. return GameTextForAll(va_return(fmat, va_start<3>), time, style);
  701. }
  702. stock va_print(const fmat[], va_args<>)
  703. {
  704. return print(va_return(fmat, va_start<1>));
  705. }
  706. stock va_fprintf(File:fhnd, const fmat[], va_args<>)
  707. {
  708. return fwrite(fhnd, va_return(fmat, va_start<2>));
  709. }
  710. stock va_SendRconCommand(const command[], va_args<>)
  711. {
  712. return SendRconCommand(va_return(command, va_start<1>));
  713. }