y_va.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  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. #include "internal\y_version"
  86. #include "internal\y_funcinc"
  87. #include "y_utils"
  88. //#define va_args<%0> %0
  89. #define va_args<%0> GLOBAL_TAG_TYPES:...
  90. #define va_start<%0> (va_:(%0))
  91. stock va_printf(const fmat[], va_:STATIC_ARGS)
  92. {
  93. new
  94. num_args,
  95. arg_start,
  96. arg_end;
  97. // Get the pointer to the number of arguments to the last function.
  98. #emit LOAD.S.pri 0
  99. #emit ADD.C 8
  100. #emit MOVE.alt
  101. // Get the number of arguments.
  102. #emit LOAD.I
  103. #emit STOR.S.pri num_args
  104. // Get the variable arguments (end).
  105. #emit ADD
  106. #emit STOR.S.pri arg_end
  107. // Get the variable arguments (start).
  108. #emit LOAD.S.pri STATIC_ARGS
  109. #emit SMUL.C 4
  110. #emit ADD
  111. #emit STOR.S.pri arg_start
  112. // Using an assembly loop here screwed the code up as the labels added some
  113. // odd stack/frame manipulation code...
  114. while (arg_end != arg_start)
  115. {
  116. #emit MOVE.pri
  117. #emit LOAD.I
  118. #emit PUSH.pri
  119. #emit CONST.pri 4
  120. #emit SUB.alt
  121. #emit STOR.S.pri arg_end
  122. }
  123. // Push the additional parameters.
  124. #emit PUSH.S fmat
  125. // Push the argument count.
  126. #emit LOAD.S.pri num_args
  127. #emit ADD.C 4
  128. #emit LOAD.S.alt STATIC_ARGS
  129. #emit XCHG
  130. #emit SMUL.C 4
  131. #emit SUB.alt
  132. #emit PUSH.pri
  133. #emit MOVE.alt
  134. // This gets confused if you have a local variable of the same name as it
  135. // seems to factor in them first, so you get the offset of the local
  136. // variable instead of the index of the native.
  137. #emit SYSREQ.C printf
  138. // Clear the stack.
  139. #emit CONST.pri 4
  140. #emit ADD
  141. #emit MOVE.alt
  142. // The three lines above get the total stack data size, now remove it.
  143. #emit LCTRL 4
  144. #emit ADD
  145. #emit SCTRL 4
  146. // Now do the real return.
  147. }
  148. stock va_CallRemoteFunction(const function[], const fmat[], va_:STATIC_ARGS)
  149. {
  150. new
  151. num_args,
  152. arg_start,
  153. arg_end;
  154. // Get the pointer to the number of arguments to the last function.
  155. #emit LOAD.S.pri 0
  156. #emit ADD.C 8
  157. #emit MOVE.alt
  158. // Get the number of arguments.
  159. #emit LOAD.I
  160. #emit STOR.S.pri num_args
  161. // Get the variable arguments (end).
  162. #emit ADD
  163. #emit STOR.S.pri arg_end
  164. // Get the variable arguments (start).
  165. #emit LOAD.S.pri STATIC_ARGS
  166. #emit SMUL.C 4
  167. #emit ADD
  168. #emit STOR.S.pri arg_start
  169. // Using an assembly loop here screwed the code up as the labels added some
  170. // odd stack/frame manipulation code...
  171. while (arg_end != arg_start)
  172. {
  173. #emit MOVE.pri
  174. #emit LOAD.I
  175. #emit PUSH.pri
  176. #emit CONST.pri 4
  177. #emit SUB.alt
  178. #emit STOR.S.pri arg_end
  179. }
  180. // Push the additional parameters.
  181. #emit PUSH.S fmat
  182. #emit PUSH.S function
  183. // Push the argument count.
  184. #emit LOAD.S.pri num_args
  185. #emit ADD.C 8
  186. #emit LOAD.S.alt STATIC_ARGS
  187. #emit XCHG
  188. #emit SMUL.C 4
  189. #emit SUB.alt
  190. #emit PUSH.pri
  191. #emit MOVE.alt
  192. // This gets confused if you have a local variable of the same name as it
  193. // seems to factor in them first, so you get the offset of the local
  194. // variable instead of the index of the native.
  195. #emit SYSREQ.C CallRemoteFunction
  196. // Clear the stack.
  197. #emit CONST.pri 4
  198. #emit ADD
  199. #emit MOVE.alt
  200. // The three lines above get the total stack data size, now remove it.
  201. #emit LCTRL 4
  202. #emit ADD
  203. #emit SCTRL 4
  204. // Now do the real return.
  205. }
  206. stock va_CallLocalFunction(const function[], const fmat[], va_:STATIC_ARGS)
  207. {
  208. new
  209. num_args,
  210. arg_start,
  211. arg_end;
  212. // Get the pointer to the number of arguments to the last function.
  213. #emit LOAD.S.pri 0
  214. #emit ADD.C 8
  215. #emit MOVE.alt
  216. // Get the number of arguments.
  217. #emit LOAD.I
  218. #emit STOR.S.pri num_args
  219. // Get the variable arguments (end).
  220. #emit ADD
  221. #emit STOR.S.pri arg_end
  222. // Get the variable arguments (start).
  223. #emit LOAD.S.pri STATIC_ARGS
  224. #emit SMUL.C 4
  225. #emit ADD
  226. #emit STOR.S.pri arg_start
  227. // Using an assembly loop here screwed the code up as the labels added some
  228. // odd stack/frame manipulation code...
  229. while (arg_end != arg_start)
  230. {
  231. #emit MOVE.pri
  232. #emit LOAD.I
  233. #emit PUSH.pri
  234. #emit CONST.pri 4
  235. #emit SUB.alt
  236. #emit STOR.S.pri arg_end
  237. }
  238. // Push the additional parameters.
  239. #emit PUSH.S fmat
  240. #emit PUSH.S function
  241. // Push the argument count.
  242. #emit LOAD.S.pri num_args
  243. #emit ADD.C 8
  244. #emit LOAD.S.alt STATIC_ARGS
  245. #emit XCHG
  246. #emit SMUL.C 4
  247. #emit SUB.alt
  248. #emit PUSH.pri
  249. #emit MOVE.alt
  250. // This gets confused if you have a local variable of the same name as it
  251. // seems to factor in them first, so you get the offset of the local
  252. // variable instead of the index of the native.
  253. #emit SYSREQ.C CallLocalFunction
  254. // Clear the stack.
  255. #emit CONST.pri 4
  256. #emit ADD
  257. #emit MOVE.alt
  258. // The three lines above get the total stack data size, now remove it.
  259. #emit LCTRL 4
  260. #emit ADD
  261. #emit SCTRL 4
  262. // Now do the real return.
  263. }
  264. stock va_SetTimerEx(const function[], interval, bool:repeating, const fmat[], va_:STATIC_ARGS)
  265. {
  266. new
  267. num_args,
  268. arg_start,
  269. arg_end;
  270. // Get the pointer to the number of arguments to the last function.
  271. #emit LOAD.S.pri 0
  272. #emit ADD.C 8
  273. #emit MOVE.alt
  274. // Get the number of arguments.
  275. #emit LOAD.I
  276. #emit STOR.S.pri num_args
  277. // Get the variable arguments (end).
  278. #emit ADD
  279. #emit STOR.S.pri arg_end
  280. // Get the variable arguments (start).
  281. #emit LOAD.S.pri STATIC_ARGS
  282. #emit SMUL.C 4
  283. #emit ADD
  284. #emit STOR.S.pri arg_start
  285. // Using an assembly loop here screwed the code up as the labels added some
  286. // odd stack/frame manipulation code...
  287. while (arg_end != arg_start)
  288. {
  289. #emit MOVE.pri
  290. #emit LOAD.I
  291. #emit PUSH.pri
  292. #emit CONST.pri 4
  293. #emit SUB.alt
  294. #emit STOR.S.pri arg_end
  295. }
  296. // Push the additional parameters.
  297. #emit PUSH.S fmat
  298. #emit PUSH.S repeating
  299. #emit PUSH.S interval
  300. #emit PUSH.S function
  301. // Push the argument count.
  302. #emit LOAD.S.pri num_args
  303. #emit ADD.C 16
  304. #emit LOAD.S.alt STATIC_ARGS
  305. #emit XCHG
  306. #emit SMUL.C 4
  307. #emit SUB.alt
  308. #emit PUSH.pri
  309. #emit MOVE.alt
  310. // This gets confused if you have a local variable of the same name as it
  311. // seems to factor in them first, so you get the offset of the local
  312. // variable instead of the index of the native.
  313. #emit SYSREQ.C SetTimerEx
  314. // Clear the stack.
  315. #emit CONST.pri 4
  316. #emit ADD
  317. #emit MOVE.alt
  318. // The three lines above get the total stack data size, now remove it.
  319. #emit LCTRL 4
  320. #emit ADD
  321. #emit SCTRL 4
  322. // Now do the real return.
  323. }
  324. stock va_format(out[], size, const fmat[], va_:STATIC_ARGS)
  325. {
  326. new
  327. num_args,
  328. arg_start,
  329. arg_end;
  330. // Get the pointer to the number of arguments to the last function.
  331. #emit LOAD.S.pri 0
  332. #emit ADD.C 8
  333. #emit MOVE.alt
  334. // Get the number of arguments.
  335. #emit LOAD.I
  336. #emit STOR.S.pri num_args
  337. // Get the variable arguments (end).
  338. #emit ADD
  339. #emit STOR.S.pri arg_end
  340. // Get the variable arguments (start).
  341. #emit LOAD.S.pri STATIC_ARGS
  342. #emit SMUL.C 4
  343. #emit ADD
  344. #emit STOR.S.pri arg_start
  345. // Using an assembly loop here screwed the code up as the labels added some
  346. // odd stack/frame manipulation code...
  347. while (arg_end != arg_start)
  348. {
  349. #emit MOVE.pri
  350. #emit LOAD.I
  351. #emit PUSH.pri
  352. #emit CONST.pri 4
  353. #emit SUB.alt
  354. #emit STOR.S.pri arg_end
  355. }
  356. // Push the additional parameters.
  357. #emit PUSH.S fmat
  358. #emit PUSH.S size
  359. #emit PUSH.S out
  360. // Push the argument count.
  361. #emit LOAD.S.pri num_args
  362. #emit ADD.C 12
  363. #emit LOAD.S.alt STATIC_ARGS
  364. #emit XCHG
  365. #emit SMUL.C 4
  366. #emit SUB.alt
  367. #emit PUSH.pri
  368. #emit MOVE.alt
  369. // This gets confused if you have a local variable of the same name as it
  370. // seems to factor in them first, so you get the offset of the local
  371. // variable instead of the index of the native.
  372. #emit SYSREQ.C format
  373. // Clear the stack.
  374. #emit CONST.pri 4
  375. #emit ADD
  376. #emit MOVE.alt
  377. // The three lines above get the total stack data size, now remove it.
  378. #emit LCTRL 4
  379. #emit ADD
  380. #emit SCTRL 4
  381. // Now do the real return.
  382. }
  383. stock va_return(const fmat[], va_:STATIC_ARGS)
  384. {
  385. static
  386. out[YSI_MAX_STRING * 8],
  387. size = YSI_MAX_STRING;
  388. new
  389. num_args,
  390. arg_start,
  391. arg_end;
  392. // Get the pointer to the number of arguments to the last function.
  393. #emit LOAD.S.pri 0
  394. #emit ADD.C 8
  395. #emit MOVE.alt
  396. // Get the number of arguments.
  397. #emit LOAD.I
  398. #emit STOR.S.pri num_args
  399. // Get the variable arguments (end).
  400. #emit ADD
  401. #emit STOR.S.pri arg_end
  402. // Get the variable arguments (start).
  403. #emit LOAD.S.pri STATIC_ARGS
  404. #emit SMUL.C 4
  405. #emit ADD
  406. #emit STOR.S.pri arg_start
  407. // Using an assembly loop here screwed the code up as the labels added some
  408. // odd stack/frame manipulation code...
  409. while (arg_end != arg_start)
  410. {
  411. #emit MOVE.pri
  412. #emit LOAD.I
  413. #emit PUSH.pri
  414. #emit CONST.pri 4
  415. #emit SUB.alt
  416. #emit STOR.S.pri arg_end
  417. }
  418. // Push the additional parameters.
  419. #emit PUSH.S fmat
  420. #emit PUSH size
  421. #emit PUSH.C out
  422. // Push the argument count.
  423. #emit LOAD.S.pri num_args
  424. #emit ADD.C 12
  425. #emit LOAD.S.alt STATIC_ARGS
  426. #emit XCHG
  427. #emit SMUL.C 4
  428. #emit SUB.alt
  429. #emit PUSH.pri
  430. #emit MOVE.alt
  431. // This gets confused if you have a local variable of the same name as it
  432. // seems to factor in them first, so you get the offset of the local
  433. // variable instead of the index of the native.
  434. #emit SYSREQ.C format
  435. // Clear the stack.
  436. #emit CONST.pri 4
  437. #emit ADD
  438. #emit MOVE.alt
  439. // The three lines above get the total stack data size, now remove it.
  440. #emit LCTRL 4
  441. #emit ADD
  442. #emit SCTRL 4
  443. // Now do the real return.
  444. return out;
  445. }
  446. stock va_strlen(arg)
  447. {
  448. // Get the length of the string at the given position on the previous
  449. // function's stack (convenience function).
  450. // Get the address of the previous function's stack. First get the index of
  451. // the argument required.
  452. #emit LOAD.S.pri arg
  453. // Then convert that number to bytes from cells.
  454. #emit SMUL.C 4
  455. // Get the previous function's frame. Stored in variable 0 (in the current
  456. // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is
  457. // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add
  458. // checks that "arg * 4 < *(*(FRM + 0) + 8)", for the previous frame parameter
  459. // count (in C pointer speak).
  460. #emit LOAD.S.alt 0
  461. // Add the frame pointer to the argument offset in bytes.
  462. #emit ADD
  463. // Add 12 to skip over the function header.
  464. #emit ADD.C 12
  465. // Load the address stored in the specified address.
  466. #emit LOAD.I
  467. // Push the address we just determined was the source.
  468. #emit PUSH.pri
  469. // Push the number of parameters passed (in bytes) to the function.
  470. #emit PUSH.C 4
  471. // Call the function.
  472. #emit SYSREQ.C strlen
  473. // Restore the stack to its level before we called this native.
  474. #emit STACK 8
  475. #emit RETN
  476. // Never called.
  477. return 0;
  478. }
  479. stock va_getstring(dest[], arg, len = sizeof (dest))
  480. {
  481. // Get the address of the previous function's stack. First get the index of
  482. // the argument required.
  483. #emit LOAD.S.pri arg
  484. // Then convert that number to bytes from cells.
  485. #emit SMUL.C 4
  486. // Get the previous function's frame. Stored in variable 0 (in the current
  487. // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is
  488. // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add
  489. // checks that "arg * 4 < *(*(FRM + 0) + 8)", for the previous frame parameter
  490. // count (in C pointer speak).
  491. #emit LOAD.S.alt 0
  492. // Add the frame pointer to the argument offset in bytes.
  493. #emit ADD
  494. // Add 12 to skip over the function header.
  495. #emit ADD.C 12
  496. // Load the address stored in the specified address.
  497. #emit LOAD.I
  498. // Push the length for "strcat".
  499. #emit PUSH.S len
  500. // Push the address we just determined was the source.
  501. #emit PUSH.pri
  502. // Load the address of the destination.
  503. #emit LOAD.S.alt dest
  504. // Blank the first cell so "strcat" behaves like "strcpy".
  505. #emit CONST.pri 0
  506. // Store the loaded number 0 to the loaded address.
  507. #emit STOR.I
  508. // Push the loaded address.
  509. #emit PUSH.alt
  510. // Push the number of parameters passed (in bytes) to the function.
  511. #emit PUSH.C 12
  512. // Call the function.
  513. #emit SYSREQ.C strcat
  514. // Restore the stack to its level before we called this native.
  515. #emit STACK 16
  516. }
  517. stock va_SendClientMessage(playerid, colour, const fmat[], va_args<>)
  518. {
  519. return SendClientMessage(playerid, colour, va_return(fmat, va_start<3>));
  520. }
  521. stock va_SendClientMessageToAll(colour, const fmat[], va_args<>)
  522. {
  523. return SendClientMessageToAll(colour, va_return(fmat, va_start<2>));
  524. }
  525. stock va_SendPlayerMessageToPlayer(playerid, senderid, const fmat[], va_args<>)
  526. {
  527. return SendPlayerMessageToPlayer(playerid, senderid, va_return(fmat, va_start<3>));
  528. }
  529. stock va_SendPlayerMessageToAll(senderid, const fmat[], va_args<>)
  530. {
  531. return SendPlayerMessageToAll(senderid, va_return(fmat, va_start<2>));
  532. }
  533. stock va_GameTextForPlayer(playerid, const fmat[], time, style, va_args<>)
  534. {
  535. return GameTextForPlayer(playerid, va_return(fmat, va_start<4>), time, style);
  536. }
  537. stock va_GameTextForAll(const fmat[], time, style, va_args<>)
  538. {
  539. return GameTextForAll(va_return(fmat, va_start<3>), time, style);
  540. }
  541. stock va_print(const fmat[], va_args<>)
  542. {
  543. return print(va_return(fmat, va_start<1>));
  544. }