impl.inc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /**--------------------------------------------------------------------------**\
  2. ==============================
  3. y_va - Enhanced vararg code!
  4. ==============================
  5. Description:
  6. This library currently provides two functions - va_printf and va_format
  7. which perform printf and format using variable arguments passed to another
  8. function.
  9. This is bsed on the variable parameter passing method based on code by Zeex.
  10. See page 15 of the code optimisations topic.
  11. Legal:
  12. Version: MPL 1.1
  13. The contents of this file are subject to the Mozilla Public License Version
  14. 1.1 (the "License"); you may not use this file except in compliance with
  15. the License. You may obtain a copy of the License at
  16. http://www.mozilla.org/MPL/
  17. Software distributed under the License is distributed on an "AS IS" basis,
  18. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  19. for the specific language governing rights and limitations under the
  20. License.
  21. The Original Code is the YSI vararg include.
  22. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  23. Portions created by the Initial Developer are Copyright (C) 2011
  24. the Initial Developer. All Rights Reserved.
  25. Contributors:
  26. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  27. Thanks:
  28. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  29. ZeeX - Very productive conversations.
  30. koolk - IsPlayerinAreaEx code.
  31. TheAlpha - Danish translation.
  32. breadfish - German translation.
  33. Fireburn - Dutch translation.
  34. yom - French translation.
  35. 50p - Polish translation.
  36. Zamaroht - Spanish translation.
  37. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  38. for me to strive to better.
  39. Pixels^ - Running XScripters where the idea was born.
  40. Matite - Pestering me to release it and using it.
  41. Very special thanks to:
  42. Thiadmer - PAWN, whose limits continue to amaze me!
  43. Kye/Kalcor - SA:MP.
  44. SA:MP Team past, present and future - SA:MP.
  45. Version:
  46. 1.0
  47. Changelog:
  48. 02/05/11:
  49. First version.
  50. Functions:
  51. Public:
  52. -
  53. Core:
  54. -
  55. Stock:
  56. -
  57. Static:
  58. -
  59. Inline:
  60. -
  61. API:
  62. -
  63. Callbacks:
  64. -
  65. Definitions:
  66. -
  67. Enums:
  68. -
  69. Macros:
  70. -
  71. Tags:
  72. -
  73. Variables:
  74. Global:
  75. -
  76. Static:
  77. -
  78. Commands:
  79. -
  80. Compile options:
  81. -
  82. Operators:
  83. -
  84. \**--------------------------------------------------------------------------**/
  85. static stock
  86. YSI_g_sHeap,
  87. YSI_g_sStack,
  88. YSI_g_sArgCount,
  89. YSI_g_sArgs[6];
  90. stock va_printf(const fmat[], va_:STATIC_ARGS)
  91. {
  92. // Get the number of parameters. Plus an extra one because the JIT pushes
  93. // its own value on to the stack (won't make any difference when the JIT
  94. // isn't in use).
  95. #emit LOAD.S.alt STATIC_ARGS
  96. #emit INC.alt // 2 - n
  97. #emit SHL.C.alt 2
  98. // "alt" now contains the number of static arguments in bytes - 4.
  99. // Get the previous parameter count.
  100. #emit LOAD.S.pri 0
  101. #emit ADD.C 8
  102. #emit LOAD.I
  103. #emit SUB
  104. #emit ADD.C 8
  105. #emit STOR.pri YSI_g_sArgCount // "printf"s parameter count.
  106. // Get the address of the arguments we are replacing.
  107. #emit LOAD.S.pri 0
  108. #emit ADD
  109. #emit ADD.C 0xFFFFFFFC // -4 adjustment for JIT.
  110. // Copy them to our temporary buffer.
  111. #emit CONST.alt YSI_g_sArgs
  112. #emit MOVS 12 // (n + 3) * 4
  113. // Go to the new "top" of the stack.
  114. #emit STACK 0
  115. #emit STOR.alt YSI_g_sStack // Store it.
  116. #emit ADD.C 12 // (n + 3) * 4
  117. #emit SCTRL 4
  118. // "frm" is still valid.
  119. #emit PUSH.S fmat // Get the final parameter.
  120. #emit PUSH YSI_g_sArgCount // Push the parameter count.
  121. // Call the function.
  122. #emit SYSREQ.C printf
  123. #emit STOR.pri YSI_g_sArgCount
  124. // Copy the data back.
  125. #emit STACK 0xFFFFFFFC // -4 adjustment for JIT.
  126. #emit STACK 0
  127. #emit LOAD.pri YSI_g_sStack
  128. #emit SCTRL 4
  129. #emit CONST.pri YSI_g_sArgs
  130. #emit MOVS 12
  131. #emit LOAD.pri YSI_g_sArgCount
  132. #emit RETN
  133. // This new version of the code worked first time! I'm very happy with that
  134. // result, but I also feel it was justified given how long I spent staring
  135. // at it!
  136. return 0;
  137. }
  138. stock va_CallRemoteFunction(const function[], const fmat[], va_:STATIC_ARGS)
  139. {
  140. // This function can't use the same stack manipulation tricks as most of the
  141. // other native function calls, since the native calls back in to user code
  142. // and so will use more of the stack. The other native calls are fast
  143. // because they unwind part of the stack, use the parameters from an earlier
  144. // function call to call the native, then restore the stack to where it was
  145. // before. If this does that, local variables and frame headers will be
  146. // overwritten by the called functions. This problem also affects
  147. // "CallLocalFunction", but does NOT affect "SetTimerEx", because that gets
  148. // a totally fresh stack for the called function when it eventually is
  149. // called. Ideally, this would use "MOVS" rewriting to do the data copying,
  150. // but that method is not JIT compatible, so instead we must use "memcpy".
  151. // Get the number of parameters.
  152. #emit LOAD.S.alt STATIC_ARGS
  153. #emit SHL.C.alt 2
  154. #emit STOR.alt YSI_g_sArgCount
  155. // "alt" now contains the number of static arguments in bytes.
  156. // Get the previous parameter count.
  157. #emit LOAD.S.pri 0
  158. #emit ADD.C 8
  159. #emit LOAD.I
  160. // "pri" now contains the number of total arguments in bytes.
  161. // Subtract the number of static bytes, and allocate the memory for the
  162. // variable arguments.
  163. #emit SUB
  164. // "pri" now contains the number of dynamic arguments in bytes.
  165. #emit STOR.pri YSI_g_sStack
  166. #emit STACK 0
  167. #emit SUB.alt
  168. #emit SCTRL 4
  169. // Copy the data.
  170. #emit LOAD.alt YSI_g_sArgCount
  171. #emit LOAD.S.pri 0
  172. #emit ADD.C 12
  173. #emit ADD
  174. // "pri" now contains a pointer to the dynamic arguments.
  175. // Call "memcpy". The final argument (the first push) is stupid big, way
  176. // bigger than it needs to be, but we know the destination is large enough
  177. // so there's no need to check and pass an accurate value, instead we can
  178. // just pass a "large enough" constant.
  179. #emit STACK 0
  180. #emit PUSH.C 1000000
  181. #emit PUSH YSI_g_sStack
  182. #emit PUSH.C 0
  183. #emit PUSH.pri
  184. #emit PUSH.alt
  185. #emit PUSH.C 20
  186. #emit SYSREQ.C memcpy
  187. #emit STACK 24
  188. // Call "CallRemoteFunction"
  189. #emit PUSH.S fmat
  190. #emit PUSH.S function
  191. #emit LOAD.pri YSI_g_sStack
  192. #emit ADD.C 8
  193. #emit PUSH.pri
  194. #emit SYSREQ.C CallRemoteFunction
  195. // Pop the stack.
  196. #emit STOR.pri YSI_g_sArgCount
  197. #emit POP.pri
  198. #emit STACK 0
  199. #emit ADD
  200. #emit SCTRL 4
  201. #emit LOAD.pri YSI_g_sArgCount
  202. #emit RETN
  203. return 0;
  204. }
  205. stock va_CallLocalFunction(const function[], const fmat[], va_:STATIC_ARGS)
  206. {
  207. // This function can't use the same stack manipulation tricks as most of the
  208. // other native function calls, since the native calls back in to user code
  209. // and so will use more of the stack. The other native calls are fast
  210. // because they unwind part of the stack, use the parameters from an earlier
  211. // function call to call the native, then restore the stack to where it was
  212. // before. If this does that, local variables and frame headers will be
  213. // overwritten by the called functions. This problem also affects
  214. // "CallRemoteFunction", but does NOT affect "SetTimerEx", because that gets
  215. // a totally fresh stack for the called function when it eventually is
  216. // called. Ideally, this would use "MOVS" rewriting to do the data copying,
  217. // but that method is not JIT compatible, so instead we must use "memcpy".
  218. // Get the number of parameters.
  219. #emit LOAD.S.alt STATIC_ARGS
  220. #emit SHL.C.alt 2
  221. #emit STOR.alt YSI_g_sArgCount
  222. // "alt" now contains the number of static arguments in bytes.
  223. // Get the previous parameter count.
  224. #emit LOAD.S.pri 0
  225. #emit ADD.C 8
  226. #emit LOAD.I
  227. // "pri" now contains the number of total arguments in bytes.
  228. // Subtract the number of static bytes, and allocate the memory for the
  229. // variable arguments.
  230. #emit SUB
  231. // "pri" now contains the number of dynamic arguments in bytes.
  232. #emit STOR.pri YSI_g_sStack
  233. #emit STACK 0
  234. #emit SUB.alt
  235. #emit SCTRL 4
  236. // Copy the data.
  237. #emit LOAD.alt YSI_g_sArgCount
  238. #emit LOAD.S.pri 0
  239. #emit ADD.C 12
  240. #emit ADD
  241. // "pri" now contains a pointer to the dynamic arguments.
  242. // Call "memcpy". The final argument (the first push) is stupid big, way
  243. // bigger than it needs to be, but we know the destination is large enough
  244. // so there's no need to check and pass an accurate value, instead we can
  245. // just pass a "large enough" constant.
  246. #emit STACK 0
  247. #emit PUSH.C 1000000
  248. #emit PUSH YSI_g_sStack
  249. #emit PUSH.C 0
  250. #emit PUSH.pri
  251. #emit PUSH.alt
  252. #emit PUSH.C 20
  253. #emit SYSREQ.C memcpy
  254. #emit STACK 24
  255. // Call "CallLocalFunction"
  256. #emit PUSH.S fmat
  257. #emit PUSH.S function
  258. #emit LOAD.pri YSI_g_sStack
  259. #emit ADD.C 8
  260. #emit PUSH.pri
  261. #emit SYSREQ.C CallLocalFunction
  262. // Pop the stack.
  263. #emit MOVE.alt
  264. #emit LCTRL 5
  265. #emit SCTRL 4
  266. #emit MOVE.pri
  267. #emit RETN
  268. return 0;
  269. }
  270. stock va_SetTimerEx(const function[], interval, bool:repeating, const fmat[], va_:STATIC_ARGS)
  271. {
  272. // Get the number of parameters.
  273. #emit LOAD.S.alt STATIC_ARGS
  274. #emit DEC.alt // 2 - n
  275. #emit DEC.alt
  276. #emit SHL.C.alt 2
  277. // "alt" now contains the number of static arguments in bytes - 8.
  278. // Get the previous parameter count.
  279. #emit LOAD.S.pri 0
  280. #emit ADD.C 8
  281. #emit LOAD.I
  282. #emit SUB
  283. #emit ADD.C 8
  284. #emit STOR.pri YSI_g_sArgCount // "format"s parameter count.
  285. // Get the address of the arguments we are replacing.
  286. #emit LOAD.S.pri 0
  287. #emit ADD
  288. #emit ADD.C 0xFFFFFFFC // -4 adjustment for JIT.
  289. // Copy them to our temporary buffer.
  290. #emit CONST.alt YSI_g_sArgs
  291. #emit MOVS 24 // (n + 1) * 4
  292. // Go to the new "top" of the stack.
  293. #emit STACK 0
  294. #emit STOR.alt YSI_g_sStack // Store it.
  295. #emit ADD.C 24 // (n + 1) * 4
  296. #emit SCTRL 4
  297. // "frm" is still valid.
  298. #emit PUSH.S fmat
  299. #emit PUSH.S repeating
  300. #emit PUSH.S interval
  301. #emit PUSH.S function
  302. #emit PUSH YSI_g_sArgCount // Push the parameter count.
  303. // Call the function.
  304. #emit SYSREQ.C SetTimerEx
  305. #emit STOR.pri YSI_g_sArgCount
  306. // Copy the data back.
  307. #emit STACK 0xFFFFFFFC // -4 adjustment for JIT.
  308. #emit STACK 0
  309. #emit LOAD.pri YSI_g_sStack
  310. #emit SCTRL 4
  311. #emit CONST.pri YSI_g_sArgs
  312. #emit MOVS 24
  313. #emit LOAD.pri YSI_g_sArgCount
  314. #emit RETN
  315. return 0;
  316. }
  317. stock va_format(out[], size, const fmat[], va_:STATIC_ARGS)
  318. {
  319. //P:C(if (_:STATIC_ARGS < 1) P:W("No static args found, please add a dummy local"););
  320. // Get the number of parameters.
  321. #emit LOAD.S.alt STATIC_ARGS
  322. #emit DEC.alt // 2 - n
  323. #emit SHL.C.alt 2
  324. // "alt" now contains the number of static arguments in bytes - 4.
  325. // Get the previous parameter count.
  326. #emit LOAD.S.pri 0
  327. #emit ADD.C 8
  328. #emit LOAD.I
  329. #emit SUB
  330. #emit ADD.C 8
  331. #emit STOR.pri YSI_g_sArgCount // "format"s parameter count.
  332. // Get the address of the arguments we are replacing.
  333. #emit LOAD.S.pri 0
  334. #emit ADD
  335. #emit ADD.C 0xFFFFFFFC // -4 adjustment for JIT.
  336. // Copy them to our temporary buffer.
  337. #emit CONST.alt YSI_g_sArgs
  338. #emit MOVS 20 // (n + 1) * 4
  339. // Go to the new "top" of the stack.
  340. #emit STACK 0
  341. #emit STOR.alt YSI_g_sStack // Store it.
  342. #emit ADD.C 20 // (n + 1) * 4
  343. #emit SCTRL 4
  344. // "frm" is still valid.
  345. #emit PUSH.S fmat
  346. #emit PUSH.S size
  347. #emit PUSH.S out
  348. #emit PUSH YSI_g_sArgCount // Push the parameter count.
  349. // Modify the heap to hold "locals". This is for when the destination is a
  350. // local within the function that called us.
  351. #emit HEAP 0
  352. #emit STOR.alt YSI_g_sHeap
  353. #emit LCTRL 4
  354. #emit SCTRL 2
  355. // Call the function.
  356. #emit SYSREQ.C format
  357. #emit STOR.pri YSI_g_sArgCount
  358. // Copy the data back.
  359. #emit LOAD.pri YSI_g_sHeap
  360. #emit SCTRL 2
  361. #emit STACK 0xFFFFFFFC // -4 adjustment for JIT.
  362. #emit STACK 0
  363. #emit LOAD.pri YSI_g_sStack
  364. #emit SCTRL 4
  365. #emit CONST.pri YSI_g_sArgs
  366. #emit MOVS 20
  367. #emit LOAD.pri YSI_g_sArgCount
  368. #emit RETN
  369. return 0;
  370. }
  371. stock va_return(const fmat[], va_:STATIC_ARGS)
  372. {
  373. static
  374. out[YSI_MAX_STRING * 8],
  375. size = sizeof (out);
  376. // Get the number of parameters - don't forget the hidden string return one.
  377. #emit LOAD.S.alt STATIC_ARGS
  378. #emit DEC.alt // 2 - n
  379. #emit SHL.C.alt 2
  380. // "alt" now contains the number of static arguments in bytes - 4.
  381. // Get the previous parameter count.
  382. #emit LOAD.S.pri 0
  383. #emit ADD.C 8
  384. #emit LOAD.I
  385. #emit SUB
  386. #emit ADD.C 8
  387. #emit STOR.pri YSI_g_sArgCount // "format"s parameter count.
  388. // Get the address of the arguments we are replacing.
  389. #emit LOAD.S.pri 0
  390. #emit ADD
  391. #emit ADD.C 0xFFFFFFFC // -4 adjustment for JIT.
  392. // Copy them to our temporary buffer.
  393. #emit CONST.alt YSI_g_sArgs
  394. #emit MOVS 20 // (n + 1) * 4
  395. // Go to the new "top" of the stack.
  396. #emit STACK 0
  397. #emit STOR.alt YSI_g_sStack // Store it.
  398. #emit ADD.C 20 // (n + 1) * 4
  399. #emit SCTRL 4
  400. // "frm" is still valid.
  401. #emit PUSH.S fmat
  402. #emit PUSH size
  403. #emit PUSH.S 20 // Secret return parameter.
  404. #emit PUSH YSI_g_sArgCount // Push the parameter count.
  405. // Modify the heap to hold "locals". This is for when the destination is a
  406. // local within the function that called us.
  407. #emit HEAP 0
  408. #emit STOR.alt YSI_g_sHeap
  409. #emit LCTRL 4
  410. #emit SCTRL 2
  411. // Call the function.
  412. #emit SYSREQ.C format
  413. // Copy the data back.
  414. #emit LOAD.pri YSI_g_sHeap
  415. #emit SCTRL 2
  416. #emit STACK 0xFFFFFFFC // -4 adjustment for JIT.
  417. #emit STACK 0
  418. #emit LOAD.pri YSI_g_sStack
  419. #emit SCTRL 4
  420. #emit CONST.pri YSI_g_sArgs
  421. #emit MOVS 20
  422. // Now do the real return, but without the costly string copying.
  423. #emit RETN
  424. return out;
  425. }
  426. stock va_strlen(arg)
  427. {
  428. // Get the length of the string at the given position on the previous
  429. // function's stack (convenience function).
  430. // Get the address of the previous function's stack. First get the index of
  431. // the argument required.
  432. #emit LOAD.S.pri arg
  433. // Then convert that number to bytes from cells.
  434. #emit SMUL.C 4
  435. // Get the previous function's frame. Stored in variable 0 (in the current
  436. // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is
  437. // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add
  438. // checks that "arg * 4 < *(*(FRM + 0) + 8)", for the previous frame parameter
  439. // count (in C pointer speak).
  440. #emit LOAD.S.alt 0
  441. // Add the frame pointer to the argument offset in bytes.
  442. #emit ADD
  443. // Add 12 to skip over the function header.
  444. #emit ADD.C 12
  445. // Load the address stored in the specified address.
  446. #emit LOAD.I
  447. // Push the address we just determined was the source.
  448. #emit PUSH.pri
  449. // Push the number of parameters passed (in bytes) to the function.
  450. #emit PUSH.C 4
  451. // Call the function.
  452. #emit SYSREQ.C strlen
  453. // Restore the stack to its level before we called this native.
  454. #emit STACK 8
  455. #emit RETN
  456. // Never called.
  457. return 0;
  458. }
  459. stock va_getstring(dest[], arg, len = sizeof (dest))
  460. {
  461. // Get the address of the previous function's stack. First get the index of
  462. // the argument required.
  463. #emit LOAD.S.pri arg
  464. // Then convert that number to bytes from cells.
  465. #emit SMUL.C 4
  466. // Get the previous function's frame. Stored in variable 0 (in the current
  467. // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is
  468. // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add
  469. // checks that "arg * 4 < *(*(FRM + 0) + 8)", for the previous frame parameter
  470. // count (in C pointer speak).
  471. #emit LOAD.S.alt 0
  472. // Add the frame pointer to the argument offset in bytes.
  473. #emit ADD
  474. // Add 12 to skip over the function header.
  475. #emit ADD.C 12
  476. // Load the address stored in the specified address.
  477. #emit LOAD.I
  478. // Push the length for "strcat".
  479. #emit PUSH.S len
  480. // Push the address we just determined was the source.
  481. #emit PUSH.pri
  482. // Load the address of the destination.
  483. #emit LOAD.S.alt dest
  484. // Blank the first cell so "strcat" behaves like "strcpy".
  485. #emit CONST.pri 0
  486. // Store the loaded number 0 to the loaded address.
  487. #emit STOR.I
  488. // Push the loaded address.
  489. #emit PUSH.alt
  490. // Push the number of parameters passed (in bytes) to the function.
  491. #emit PUSH.C 12
  492. // Call the function.
  493. #emit SYSREQ.C strcat
  494. // Restore the stack to its level before we called this native.
  495. #emit STACK 16
  496. }
  497. stock PlayerText:va_CreatePlayerTextDraw(playerid, Float:x, Float:y, fmat[], va_args<>)
  498. {
  499. return CreatePlayerTextDraw(playerid, x, y, va_return(fmat, va_start<4>));
  500. }
  501. stock Text:va_TextDrawCreate(Float:x, Float:y, fmat[], va_args<>)
  502. {
  503. return TextDrawCreate(x, y, va_return(fmat, va_start<3>));
  504. }
  505. stock va_SendClientMessage(playerid, colour, const fmat[], va_args<>)
  506. {
  507. return SendClientMessage(playerid, colour, va_return(fmat, va_start<3>));
  508. }
  509. stock va_SendClientMessageToAll(colour, const fmat[], va_args<>)
  510. {
  511. return SendClientMessageToAll(colour, va_return(fmat, va_start<2>));
  512. }
  513. stock va_SendPlayerMessageToPlayer(playerid, senderid, const fmat[], va_args<>)
  514. {
  515. return SendPlayerMessageToPlayer(playerid, senderid, va_return(fmat, va_start<3>));
  516. }
  517. stock va_SendPlayerMessageToAll(senderid, const fmat[], va_args<>)
  518. {
  519. return SendPlayerMessageToAll(senderid, va_return(fmat, va_start<2>));
  520. }
  521. stock va_GameTextForPlayer(playerid, const fmat[], time, style, va_args<>)
  522. {
  523. return GameTextForPlayer(playerid, va_return(fmat, va_start<4>), time, style);
  524. }
  525. stock va_GameTextForAll(const fmat[], time, style, va_args<>)
  526. {
  527. return GameTextForAll(va_return(fmat, va_start<3>), time, style);
  528. }
  529. stock va_print(const fmat[], va_args<>)
  530. {
  531. return print(va_return(fmat, va_start<1>));
  532. }
  533. stock va_fprintf(File:fhnd, const fmat[], va_args<>)
  534. {
  535. return fwrite(fhnd, va_return(fmat, va_start<2>));
  536. }