PageRenderTime 55ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/xbmc/visualizations/Vortex/angelscript/angelscript/source/as_callfunc_ppc_64.cpp

http://github.com/xbmc/xbmc
C++ | 999 lines | 744 code | 88 blank | 167 comment | 94 complexity | ecc02092420cee87c8f1f1eb9abd795a MD5 | raw file
Possible License(s): GPL-3.0, CC-BY-SA-3.0, LGPL-2.0, 0BSD, Unlicense, GPL-2.0, AGPL-1.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0
  1. /*
  2. AngelCode Scripting Library
  3. Copyright (c) 2003-2009 Andreas Jonsson
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any
  6. damages arising from the use of this software.
  7. Permission is granted to anyone to use this software for any
  8. purpose, including commercial applications, and to alter it and
  9. redistribute it freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you
  11. must not claim that you wrote the original software. If you use
  12. this software in a product, an acknowledgment in the product
  13. documentation would be appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and
  15. must not be misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source
  17. distribution.
  18. The original version of this library can be located at:
  19. http://www.angelcode.com/angelscript/
  20. Andreas Jonsson
  21. andreas@angelcode.com
  22. */
  23. //
  24. // as_callfunc_ppc_64.cpp
  25. //
  26. // These functions handle the actual calling of system functions
  27. //
  28. // This version is 64 bit PPC specific
  29. //
  30. #include "as_config.h"
  31. #ifndef AS_MAX_PORTABILITY
  32. #ifdef AS_PPC_64
  33. #include "as_callfunc.h"
  34. #include "as_scriptengine.h"
  35. #include "as_texts.h"
  36. #include "as_tokendef.h"
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. BEGIN_AS_NAMESPACE
  40. // This part was written and tested by Jeff Slutter
  41. // from Reactor Zero, Abril, 2007, for PlayStation 3, which
  42. // is a PowerPC 64bit based architecture. Even though it is
  43. // 64bit it seems the pointer size is still 32bit.
  44. // It still remains to be seen how well this code works
  45. // on other PPC platforms, such as XBox 360, GameCube.
  46. #define AS_PPC_MAX_ARGS 32
  47. // The array used to send values to the correct places.
  48. // Contains a byte of argTypes to indicate the register type to load
  49. // or zero if end of arguments
  50. // The +1 is for when CallThis (object methods) is used
  51. // Extra +1 when returning in memory
  52. // Extra +1 in ppcArgsType to ensure zero end-of-args marker
  53. // TODO: multithread: The global variables must be removed to make the code thread safe
  54. extern "C"
  55. {
  56. enum argTypes { ppcENDARG = 0, ppcINTARG = 1, ppcFLOATARG = 2, ppcDOUBLEARG = 3, ppcLONGARG = 4 };
  57. static asBYTE ppcArgsType[AS_PPC_MAX_ARGS + 1 + 1 + 1];
  58. static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1];
  59. }
  60. // NOTE: these values are for PowerPC 64 bit. I'm sure things are different for PowerPC 32bit, but I don't have one.
  61. // I'm pretty sure that PPC 32bit sets up a stack frame slightly different (only 24 bytes for linkage area for instance)
  62. #define PPC_LINKAGE_SIZE (0x30) // how big the PPC linkage area is in a stack frame
  63. #define PPC_NUM_REGSTORE (10) // how many registers of the PPC we need to store/restore for ppcFunc64()
  64. #define PPC_REGSTORE_SIZE (8*PPC_NUM_REGSTORE) // how many bytes are required for register store/restore
  65. #define EXTRA_STACK_SIZE (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame
  66. #define PPC_STACK_SIZE(numParams) ( -(( ( (((numParams)<8)?8:(numParams))<<3) + EXTRA_STACK_SIZE + 15 ) & ~15) ) // calculates the total stack size needed for ppcFunc64, must pad to 16bytes
  67. // This is PowerPC 64 bit specific
  68. // Loads all data into the correct places and calls the function.
  69. // ppcArgsType is an array containing a byte type (enum argTypes) for each argument.
  70. // StackArgSizeInBytes is the size in bytes of the stack frame (takes into account linkage area, etc. must be multiple of 16)
  71. extern "C" asQWORD ppcFunc64(const asDWORD* argsPtr, int StackArgSizeInBytes, asDWORD func);
  72. asm(""
  73. ".text\n"
  74. ".align 4\n"
  75. ".p2align 4,,15\n"
  76. ".globl .ppcFunc64\n"
  77. ".ppcFunc64:\n"
  78. // function prolog
  79. "std %r22, -0x08(%r1)\n" // we need a register other than r0, to store the old stack pointer
  80. "mr %r22, %r1\n" // store the old stack pointer, for now (to make storing registers easier)
  81. "stdux %r1, %r1, %r4\n" // atomically store and update the stack pointer for the new stack frame (in case of a signal/interrupt)
  82. "mflr %r0\n" // get the caller's LR register
  83. "std %r0, 0x10(%r22)\n" // store the caller's LR register
  84. "std %r23, -0x10(%r22)\n" //
  85. "std %r24, -0x18(%r22)\n" //
  86. "std %r25, -0x20(%r22)\n" //
  87. "std %r26, -0x28(%r22)\n" //
  88. "std %r27, -0x30(%r22)\n" //
  89. "std %r28, -0x38(%r22)\n" //
  90. "std %r29, -0x40(%r22)\n" //
  91. "std %r30, -0x48(%r22)\n" //
  92. "std %r31, -0x50(%r22)\n" //
  93. "std %r3, 0x30(%r22)\n" // save our parameters
  94. "std %r4, 0x38(%r22)\n" //
  95. "std %r5, 0x40(%r22)\n" //
  96. "mr %r31, %r1\n" // functions tend to store the stack pointer here too
  97. // initial registers for the function
  98. "mr %r29, %r3\n" // (r29) args list
  99. "lwz %r27, 0(%r5)\n" // load the function pointer to call. func actually holds the pointer to our function
  100. "addi %r26, %r1, 0x30\n" // setup the pointer to the parameter area to the function we're going to call
  101. "sub %r0,%r0,%r0\n" // zero out r0
  102. "mr %r23,%r0\n" // zero out r23, which holds the number of used GPR registers
  103. "mr %r22,%r0\n" // zero our r22, which holds the number of used float registers
  104. // load the global ppcArgsType which holds the types of arguments for each argument
  105. "lis %r25, ppcArgsType@ha\n" // load the upper 16 bits of the address to r25
  106. "addi %r25, %r25, ppcArgsType@l\n" // load the lower 16 bits of the address to r25
  107. "subi %r25, %r25, 1\n" // since we increment r25 on its use, we'll pre-decrement it
  108. // loop through the arguments
  109. "ppcNextArg:\n"
  110. "addi %r25, %r25, 1\n" // increment r25, our arg type pointer
  111. // switch based on the current argument type (0:end, 1:int, 2:float 3:double)
  112. "lbz %r24, 0(%r25)\n" // load the current argument type (it's a byte)
  113. "mulli %r24, %r24, 4\n" // our jump table has 4 bytes per case (1 instruction)
  114. "lis %r30, ppcTypeSwitch@ha\n" // load the address of the jump table for the switch
  115. "addi %r30, %r30, ppcTypeSwitch@l\n"
  116. "add %r0, %r30, %r24\n" // offset by our argument type
  117. "mtctr %r0\n" // load the jump address into CTR
  118. "bctr\n" // jump into the jump table/switch
  119. "nop\n"
  120. // the jump table/switch based on the current argument type
  121. "ppcTypeSwitch:\n"
  122. "b ppcArgsEnd\n"
  123. "b ppcArgIsInteger\n"
  124. "b ppcArgIsFloat\n"
  125. "b ppcArgIsDouble\n"
  126. "b ppcArgIsLong\n"
  127. // when we get here we have finished processing all the arguments
  128. // everything is ready to go to call the function
  129. "ppcArgsEnd:\n"
  130. "mtctr %r27\n" // the function pointer is stored in r27, load that into CTR
  131. "bctrl\n" // call the function. We have to do it this way so that the LR gets the proper
  132. "nop\n" // return value (the next instruction below). So we have to branch from CTR instead of LR.
  133. // when we get here, the function has returned, this is the function epilog
  134. "ld %r11,0x00(%r1)\n" // load in the caller's stack pointer
  135. "ld %r0,0x10(%r11)\n" // load in the caller's LR
  136. "mtlr %r0\n" // restore the caller's LR
  137. "ld %r22, -0x08(%r11)\n" // load registers
  138. "ld %r23, -0x10(%r11)\n" //
  139. "ld %r24, -0x18(%r11)\n" //
  140. "ld %r25, -0x20(%r11)\n" //
  141. "ld %r26, -0x28(%r11)\n" //
  142. "ld %r27, -0x30(%r11)\n" //
  143. "ld %r28, -0x38(%r11)\n" //
  144. "ld %r29, -0x40(%r11)\n" //
  145. "ld %r30, -0x48(%r11)\n" //
  146. "ld %r31, -0x50(%r11)\n" //
  147. "mr %r1, %r11\n" // restore the caller's SP
  148. "blr\n" // return back to the caller
  149. "nop\n"
  150. // Integer argument (GPR register)
  151. "ppcArgIsInteger:\n"
  152. "lis %r30,ppcLoadIntReg@ha\n" // load the address to the jump table for integer registers
  153. "addi %r30, %r30, ppcLoadIntReg@l\n"
  154. "mulli %r0, %r23, 8\n" // each item in the jump table is 2 instructions (8 bytes)
  155. "add %r0, %r0, %r30\n" // calculate ppcLoadIntReg[numUsedGPRRegs]
  156. "lwz %r30,0(%r29)\n" // load the next argument from the argument list into r30
  157. "cmpwi %r23, 8\n" // we can only load GPR3 through GPR10 (8 registers)
  158. "bgt ppcLoadIntRegUpd\n" // if we're beyond 8 GPR registers, we're in the stack, go there
  159. "mtctr %r0\n" // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers)
  160. "bctr\n" // load the argument into a GPR register
  161. "nop\n"
  162. // jump table for GPR registers, for the first 8 GPR arguments
  163. "ppcLoadIntReg:\n"
  164. "mr %r3,%r30\n" // arg0 (to r3)
  165. "b ppcLoadIntRegUpd\n"
  166. "mr %r4,%r30\n" // arg1 (to r4)
  167. "b ppcLoadIntRegUpd\n"
  168. "mr %r5,%r30\n" // arg2 (to r5)
  169. "b ppcLoadIntRegUpd\n"
  170. "mr %r6,%r30\n" // arg3 (to r6)
  171. "b ppcLoadIntRegUpd\n"
  172. "mr %r7,%r30\n" // arg4 (to r7)
  173. "b ppcLoadIntRegUpd\n"
  174. "mr %r8,%r30\n" // arg5 (to r8)
  175. "b ppcLoadIntRegUpd\n"
  176. "mr %r9,%r30\n" // arg6 (to r9)
  177. "b ppcLoadIntRegUpd\n"
  178. "mr %r10,%r30\n" // arg7 (to r10)
  179. "b ppcLoadIntRegUpd\n"
  180. // all GPR arguments still go on the stack
  181. "ppcLoadIntRegUpd:\n"
  182. "std %r30,0(%r26)\n" // store the argument into the next slot on the stack's argument list
  183. "addi %r23, %r23, 1\n" // count a used GPR register
  184. "addi %r29, %r29, 4\n" // move to the next argument on the list
  185. "addi %r26, %r26, 8\n" // adjust our argument stack pointer for the next
  186. "b ppcNextArg\n" // next argument
  187. // single Float argument
  188. "ppcArgIsFloat:\n"
  189. "lis %r30,ppcLoadFloatReg@ha\n" // get the base address of the float register jump table
  190. "addi %r30, %r30, ppcLoadFloatReg@l\n"
  191. "mulli %r0, %r22 ,8\n" // each jump table entry is 8 bytes
  192. "add %r0, %r0, %r30\n" // calculate the offset to ppcLoadFloatReg[numUsedFloatReg]
  193. "lfs 0, 0(%r29)\n" // load the next argument as a float into f0
  194. "cmpwi %r22, 13\n" // can't load more than 13 float/double registers
  195. "bgt ppcLoadFloatRegUpd\n" // if we're beyond 13 registers, just fall to inserting into the stack
  196. "mtctr %r0\n" // jump into the float jump table
  197. "bctr\n"
  198. "nop\n"
  199. // jump table for float registers, for the first 13 float arguments
  200. "ppcLoadFloatReg:\n"
  201. "fmr 1,0\n" // arg0 (f1)
  202. "b ppcLoadFloatRegUpd\n"
  203. "fmr 2,0\n" // arg1 (f2)
  204. "b ppcLoadFloatRegUpd\n"
  205. "fmr 3,0\n" // arg2 (f3)
  206. "b ppcLoadFloatRegUpd\n"
  207. "fmr 4,0\n" // arg3 (f4)
  208. "b ppcLoadFloatRegUpd\n"
  209. "fmr 5,0\n" // arg4 (f5)
  210. "b ppcLoadFloatRegUpd\n"
  211. "fmr 6,0\n" // arg5 (f6)
  212. "b ppcLoadFloatRegUpd\n"
  213. "fmr 7,0\n" // arg6 (f7)
  214. "b ppcLoadFloatRegUpd\n"
  215. "fmr 8,0\n" // arg7 (f8)
  216. "b ppcLoadFloatRegUpd\n"
  217. "fmr 9,0\n" // arg8 (f9)
  218. "b ppcLoadFloatRegUpd\n"
  219. "fmr 10,0\n" // arg9 (f10)
  220. "b ppcLoadFloatRegUpd\n"
  221. "fmr 11,0\n" // arg10 (f11)
  222. "b ppcLoadFloatRegUpd\n"
  223. "fmr 12,0\n" // arg11 (f12)
  224. "b ppcLoadFloatRegUpd\n"
  225. "fmr 13,0\n" // arg12 (f13)
  226. "b ppcLoadFloatRegUpd\n"
  227. "nop\n"
  228. // all float arguments still go on the stack
  229. "ppcLoadFloatRegUpd:\n"
  230. "stfs 0, 0x04(%r26)\n" // store, as a single float, f0 (current argument) on to the stack argument list
  231. "addi %r23, %r23, 1\n" // a float register eats up a GPR register
  232. "addi %r22, %r22, 1\n" // ...and, of course, a float register
  233. "addi %r29, %r29, 4\n" // move to the next argument in the list
  234. "addi %r26, %r26, 8\n" // move to the next stack slot
  235. "b ppcNextArg\n" // on to the next argument
  236. "nop\n"
  237. // double Float argument
  238. "ppcArgIsDouble:\n"
  239. "lis %r30, ppcLoadDoubleReg@ha\n" // load the base address of the jump table for double registers
  240. "addi %r30, %r30, ppcLoadDoubleReg@l\n"
  241. "mulli %r0, %r22, 8\n" // each slot of the jump table is 8 bytes
  242. "add %r0, %r0, %r30\n" // calculate ppcLoadDoubleReg[numUsedFloatReg]
  243. "lfd 0, 0(%r29)\n" // load the next argument, as a double float, into f0
  244. "cmpwi %r22,13\n" // the first 13 floats must go into float registers also
  245. "bgt ppcLoadDoubleRegUpd\n" // if we're beyond 13, then just put on to the stack
  246. "mtctr %r0\n" // we're under 13, first load our register
  247. "bctr\n" // jump into the jump table
  248. "nop\n"
  249. // jump table for float registers, for the first 13 float arguments
  250. "ppcLoadDoubleReg:\n"
  251. "fmr 1,0\n" // arg0 (f1)
  252. "b ppcLoadDoubleRegUpd\n"
  253. "fmr 2,0\n" // arg1 (f2)
  254. "b ppcLoadDoubleRegUpd\n"
  255. "fmr 3,0\n" // arg2 (f3)
  256. "b ppcLoadDoubleRegUpd\n"
  257. "fmr 4,0\n" // arg3 (f4)
  258. "b ppcLoadDoubleRegUpd\n"
  259. "fmr 5,0\n" // arg4 (f5)
  260. "b ppcLoadDoubleRegUpd\n"
  261. "fmr 6,0\n" // arg5 (f6)
  262. "b ppcLoadDoubleRegUpd\n"
  263. "fmr 7,0\n" // arg6 (f7)
  264. "b ppcLoadDoubleRegUpd\n"
  265. "fmr 8,0\n" // arg7 (f8)
  266. "b ppcLoadDoubleRegUpd\n"
  267. "fmr 9,0\n" // arg8 (f9)
  268. "b ppcLoadDoubleRegUpd\n"
  269. "fmr 10,0\n" // arg9 (f10)
  270. "b ppcLoadDoubleRegUpd\n"
  271. "fmr 11,0\n" // arg10 (f11)
  272. "b ppcLoadDoubleRegUpd\n"
  273. "fmr 12,0\n" // arg11 (f12)
  274. "b ppcLoadDoubleRegUpd\n"
  275. "fmr 13,0\n" // arg12 (f13)
  276. "b ppcLoadDoubleRegUpd\n"
  277. "nop\n"
  278. // all float arguments still go on the stack
  279. "ppcLoadDoubleRegUpd:\n"
  280. "stfd 0,0(%r26)\n" // store f0, as a double, into the argument list on the stack
  281. "addi %r23, %r23, 1\n" // a double float eats up one GPR
  282. "addi %r22, %r22, 1\n" // ...and, of course, a float
  283. "addi %r29, %r29, 8\n" // increment to our next argument we need to process (8 bytes for the 64bit float)
  284. "addi %r26, %r26, 8\n" // increment to the next slot on the argument list on the stack (8 bytes)
  285. "b ppcNextArg\n" // on to the next argument
  286. "nop\n"
  287. // Long (64 bit int) argument
  288. "ppcArgIsLong:\n"
  289. "lis %r30,ppcLoadLongReg@ha\n" // load the address to the jump table for integer64
  290. "addi %r30, %r30, ppcLoadLongReg@l\n"
  291. "mulli %r0, %r23, 8\n" // each item in the jump table is 2 instructions (8 bytes)
  292. "add %r0, %r0, %r30\n" // calculate ppcLoadLongReg[numUsedGPRRegs]
  293. "ld %r30,0(%r29)\n" // load the next argument from the argument list into r30
  294. "cmpwi %r23, 8\n" // we can only load GPR3 through GPR10 (8 registers)
  295. "bgt ppcLoadLongRegUpd\n" // if we're beyond 8 GPR registers, we're in the stack, go there
  296. "mtctr %r0\n" // load the address of our ppcLoadLongReg jump table (we're below 8 GPR registers)
  297. "bctr\n" // load the argument into a GPR register
  298. "nop\n"
  299. // jump table for GPR registers, for the first 8 GPR arguments
  300. "ppcLoadLongReg:\n"
  301. "mr %r3,%r30\n" // arg0 (to r3)
  302. "b ppcLoadLongRegUpd\n"
  303. "mr %r4,%r30\n" // arg1 (to r4)
  304. "b ppcLoadLongRegUpd\n"
  305. "mr %r5,%r30\n" // arg2 (to r5)
  306. "b ppcLoadLongRegUpd\n"
  307. "mr %r6,%r30\n" // arg3 (to r6)
  308. "b ppcLoadLongRegUpd\n"
  309. "mr %r7,%r30\n" // arg4 (to r7)
  310. "b ppcLoadLongRegUpd\n"
  311. "mr %r8,%r30\n" // arg5 (to r8)
  312. "b ppcLoadLongRegUpd\n"
  313. "mr %r9,%r30\n" // arg6 (to r9)
  314. "b ppcLoadLongRegUpd\n"
  315. "mr %r10,%r30\n" // arg7 (to r10)
  316. "b ppcLoadLongRegUpd\n"
  317. // all GPR arguments still go on the stack
  318. "ppcLoadLongRegUpd:\n"
  319. "std %r30,0(%r26)\n" // store the argument into the next slot on the stack's argument list
  320. "addi %r23, %r23, 1\n" // count a used GPR register
  321. "addi %r29, %r29, 8\n" // move to the next argument on the list
  322. "addi %r26, %r26, 8\n" // adjust our argument stack pointer for the next
  323. "b ppcNextArg\n" // next argument
  324. );
  325. static asDWORD GetReturnedFloat(void)
  326. {
  327. asDWORD f;
  328. asm(" stfs 1, %0\n" : "=m"(f));
  329. return f;
  330. }
  331. static asQWORD GetReturnedDouble(void)
  332. {
  333. asQWORD f;
  334. asm(" stfd 1, %0\n" : "=m"(f));
  335. return f;
  336. }
  337. // puts the arguments in the correct place in the stack array. See comments above.
  338. static void stackArgs( const asDWORD *args, const asBYTE *argsType, int &numIntArgs, int &numFloatArgs, int &numDoubleArgs, int &numLongArgs )
  339. {
  340. // initialize our offset based on any already placed arguments
  341. int i;
  342. int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2) + (numLongArgs*2);
  343. int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs + numLongArgs;
  344. int typeIndex;
  345. for( i = 0, typeIndex = 0; ; i++, typeIndex++ )
  346. {
  347. // store the type
  348. ppcArgsType[typeOffset++] = argsType[typeIndex];
  349. if( argsType[typeIndex] == ppcENDARG )
  350. break;
  351. switch( argsType[typeIndex] )
  352. {
  353. case ppcFLOATARG:
  354. {
  355. // stow float
  356. ppcArgs[argWordPos] = args[i]; // it's just a bit copy
  357. numFloatArgs++;
  358. argWordPos++; //add one word
  359. }
  360. break;
  361. case ppcDOUBLEARG:
  362. {
  363. // stow double
  364. memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment
  365. numDoubleArgs++;
  366. argWordPos+=2; //add two words
  367. i++;//doubles take up 2 argument slots
  368. }
  369. break;
  370. case ppcINTARG:
  371. {
  372. // stow register
  373. ppcArgs[argWordPos] = args[i];
  374. numIntArgs++;
  375. argWordPos++;
  376. }
  377. break;
  378. case ppcLONGARG:
  379. {
  380. // stow long
  381. memcpy( &ppcArgs[argWordPos], &args[i], 8 ); // for alignment purposes, we use memcpy
  382. numLongArgs++;
  383. argWordPos += 2; // add two words
  384. i++; // longs take up 2 argument slots
  385. }
  386. break;
  387. }
  388. }
  389. // close off the argument list (if we have max args we won't close it off until here)
  390. ppcArgsType[typeOffset] = ppcENDARG;
  391. }
  392. static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
  393. {
  394. int baseArgCount = 0;
  395. if( retInMemory )
  396. {
  397. // the first argument is the 'return in memory' pointer
  398. ppcArgs[0] = (asDWORD)retInMemory;
  399. ppcArgsType[0] = ppcINTARG;
  400. ppcArgsType[1] = ppcENDARG;
  401. baseArgCount = 1;
  402. }
  403. // put the arguments in the correct places in the ppcArgs array
  404. int numTotalArgs = baseArgCount;
  405. if( argSize > 0 )
  406. {
  407. int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
  408. stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
  409. numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs;
  410. }
  411. else
  412. {
  413. // no arguments, cap the type list
  414. ppcArgsType[baseArgCount] = ppcENDARG;
  415. }
  416. // call the function with the arguments
  417. return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
  418. }
  419. // This function is identical to CallCDeclFunction, with the only difference that
  420. // the value in the first parameter is the object (unless we are returning in memory)
  421. static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory )
  422. {
  423. int baseArgCount = 0;
  424. if( retInMemory )
  425. {
  426. // the first argument is the 'return in memory' pointer
  427. ppcArgs[0] = (asDWORD)retInMemory;
  428. ppcArgsType[0] = ppcINTARG;
  429. ppcArgsType[1] = ppcENDARG;
  430. baseArgCount = 1;
  431. }
  432. // the first argument is the 'this' of the object
  433. ppcArgs[baseArgCount] = (asDWORD)obj;
  434. ppcArgsType[baseArgCount++] = ppcINTARG;
  435. ppcArgsType[baseArgCount] = ppcENDARG;
  436. // put the arguments in the correct places in the ppcArgs array
  437. int numTotalArgs = baseArgCount;
  438. if( argSize > 0 )
  439. {
  440. int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
  441. stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
  442. numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs;
  443. }
  444. // call the function with the arguments
  445. return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func);
  446. }
  447. // This function is identical to CallCDeclFunction, with the only difference that
  448. // the value in the last parameter is the object
  449. // NOTE: on PPC the order for the args is reversed
  450. static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
  451. {
  452. UNUSED_VAR(argSize);
  453. int baseArgCount = 0;
  454. if( retInMemory )
  455. {
  456. // the first argument is the 'return in memory' pointer
  457. ppcArgs[0] = (asDWORD)retInMemory;
  458. ppcArgsType[0] = ppcINTARG;
  459. ppcArgsType[1] = ppcENDARG;
  460. baseArgCount = 1;
  461. }
  462. // stack any of the arguments
  463. int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
  464. stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
  465. int numTotalArgs = intArgs + floatArgs + doubleArgs;
  466. // can we fit the object in at the end?
  467. if( numTotalArgs < AS_PPC_MAX_ARGS )
  468. {
  469. // put the object pointer at the end
  470. int argPos = intArgs + floatArgs + (doubleArgs * 2) + (longArgs *2);
  471. ppcArgs[argPos] = (asDWORD)obj;
  472. ppcArgsType[numTotalArgs++] = ppcINTARG;
  473. ppcArgsType[numTotalArgs] = ppcENDARG;
  474. }
  475. // call the function with the arguments
  476. return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
  477. }
  478. // returns true if the given parameter is a 'variable argument'
  479. inline bool IsVariableArgument( asCDataType type )
  480. {
  481. return (type.GetTokenType() == ttQuestion) ? true : false;
  482. }
  483. int CallSystemFunction(int id, asCContext *context, void *objectPointer)
  484. {
  485. // use a working array of types, we'll configure the final one in stackArgs
  486. asBYTE argsType[AS_PPC_MAX_ARGS + 1 + 1 + 1];
  487. memset( argsType, 0, sizeof(argsType));
  488. asCScriptEngine *engine = context->engine;
  489. asCScriptFunction *descr = engine->scriptFunctions[id];
  490. asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
  491. int callConv = sysFunc->callConv;
  492. if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD )
  493. {
  494. // we're only handling native calls, handle generic calls in here
  495. return context->CallGeneric( id, objectPointer);
  496. }
  497. asQWORD retQW = 0;
  498. void *func = (void*)sysFunc->func;
  499. int paramSize = sysFunc->paramSize;
  500. int popSize = paramSize;
  501. asDWORD *args = context->regs.stackPointer;
  502. void *obj = NULL;
  503. asDWORD *vftable = NULL;
  504. void *retObjPointer = NULL; // for system functions that return AngelScript objects
  505. void *retInMemPointer = NULL; // for host functions that need to return data in memory instead of by register
  506. int a;
  507. // convert the parameters that are < 4 bytes from little endian to big endian
  508. int argDwordOffset = 0;
  509. int totalArgumentCount = 0;
  510. // if this is a THISCALL function and no object pointer was given, then the
  511. // first argument on the stack is the object pointer -- we MUST skip it for doing
  512. // the endian flipping.
  513. if( ( callConv >= ICC_THISCALL ) && (objectPointer == NULL) )
  514. {
  515. ++argDwordOffset;
  516. }
  517. for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a )
  518. {
  519. // get the size for the parameter
  520. int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes();
  521. ++totalArgumentCount;
  522. // is this a variable argument?
  523. // for variable arguments, the typeID will always follow...but we know it is 4 bytes
  524. // so we can skip that parameter automatically.
  525. bool isVarArg = IsVariableArgument( descr->parameterTypes[a] );
  526. if( isVarArg )
  527. {
  528. ++totalArgumentCount;
  529. }
  530. if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() )
  531. {
  532. // DWORD or larger parameter --- no flipping needed
  533. argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords();
  534. }
  535. else
  536. {
  537. // flip
  538. assert( numBytes == 1 || numBytes == 2 );
  539. switch( numBytes )
  540. {
  541. case 1:
  542. {
  543. volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]);
  544. asBYTE t = bPtr[0];
  545. bPtr[0] = bPtr[3];
  546. bPtr[3] = t;
  547. t = bPtr[1];
  548. bPtr[1] = bPtr[2];
  549. bPtr[2] = t;
  550. }
  551. break;
  552. case 2:
  553. {
  554. volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]);
  555. asWORD t = wPtr[0];
  556. wPtr[0] = wPtr[1];
  557. wPtr[1] = t;
  558. }
  559. break;
  560. }
  561. ++argDwordOffset;
  562. }
  563. if( isVarArg )
  564. {
  565. // skip the implicit typeID
  566. ++argDwordOffset;
  567. }
  568. }
  569. // Objects returned to AngelScript must be via an object pointer. This goes for
  570. // ALL objects, including those of simple, complex, primitive or float. Whether
  571. // the host system (PPC in this case) returns the 'object' as a pointer depends on the type of object.
  572. context->regs.objectType = descr->returnType.GetObjectType();
  573. if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() )
  574. {
  575. // Allocate the memory for the object
  576. retObjPointer = engine->CallAlloc( descr->returnType.GetObjectType() );
  577. if( sysFunc->hostReturnInMemory )
  578. {
  579. // The return is made in memory on the host system
  580. callConv++;
  581. retInMemPointer = retObjPointer;
  582. }
  583. }
  584. // make sure that host functions that will be returning in memory have a memory pointer
  585. assert( sysFunc->hostReturnInMemory==false || retInMemPointer!=NULL );
  586. if( callConv >= ICC_THISCALL )
  587. {
  588. if( objectPointer )
  589. {
  590. obj = objectPointer;
  591. }
  592. else
  593. {
  594. // The object pointer should be popped from the context stack
  595. popSize++;
  596. // Check for null pointer
  597. obj = (void*)*(args);
  598. if( obj == NULL )
  599. {
  600. context->SetInternalException(TXT_NULL_POINTER_ACCESS);
  601. if( retObjPointer )
  602. {
  603. engine->CallFree(retObjPointer);
  604. }
  605. return 0;
  606. }
  607. // Add the base offset for multiple inheritance
  608. obj = (void*)(int(obj) + sysFunc->baseOffset);
  609. // Skip the object pointer
  610. args++;
  611. }
  612. }
  613. assert( totalArgumentCount <= AS_PPC_MAX_ARGS );
  614. // mark all float/double/int arguments
  615. int argIndex = 0;
  616. for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a, ++argIndex )
  617. {
  618. // get the base type
  619. argsType[argIndex] = ppcINTARG;
  620. if( descr->parameterTypes[a].IsFloatType() && !descr->parameterTypes[a].IsReference() )
  621. {
  622. argsType[argIndex] = ppcFLOATARG;
  623. }
  624. if( descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() )
  625. {
  626. argsType[argIndex] = ppcDOUBLEARG;
  627. }
  628. if( descr->parameterTypes[a].GetSizeOnStackDWords() == 2 && !descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() )
  629. {
  630. argsType[argIndex] = ppcLONGARG;
  631. }
  632. // if it is a variable argument, account for the typeID
  633. if( IsVariableArgument(descr->parameterTypes[a]) )
  634. {
  635. // implicitly add another parameter (AFTER the parameter above), for the TypeID
  636. argsType[++argIndex] = ppcINTARG;
  637. }
  638. }
  639. assert( argIndex == totalArgumentCount );
  640. asDWORD paramBuffer[64];
  641. if( sysFunc->takesObjByVal )
  642. {
  643. paramSize = 0;
  644. int spos = 0;
  645. int dpos = 1;
  646. for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
  647. {
  648. if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
  649. {
  650. #ifdef COMPLEX_OBJS_PASSED_BY_REF
  651. if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK )
  652. {
  653. paramBuffer[dpos++] = args[spos++];
  654. ++paramSize;
  655. }
  656. else
  657. #endif
  658. {
  659. // NOTE: we may have to do endian flipping here
  660. // Copy the object's memory to the buffer
  661. memcpy( &paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() );
  662. // Delete the original memory
  663. engine->CallFree( *(char**)(args+spos) );
  664. spos++;
  665. dpos += descr->parameterTypes[n].GetSizeInMemoryDWords();
  666. paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords();
  667. }
  668. }
  669. else
  670. {
  671. // Copy the value directly
  672. paramBuffer[dpos++] = args[spos++];
  673. if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
  674. {
  675. paramBuffer[dpos++] = args[spos++];
  676. }
  677. paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
  678. }
  679. // if this was a variable argument parameter, then account for the implicit typeID
  680. if( IsVariableArgument( descr->parameterTypes[n] ) )
  681. {
  682. // the TypeID is just a DWORD
  683. paramBuffer[dpos++] = args[spos++];
  684. ++paramSize;
  685. }
  686. }
  687. // Keep a free location at the beginning
  688. args = &paramBuffer[1];
  689. }
  690. // one last verification to make sure things are how we expect
  691. assert( (retInMemPointer!=NULL && sysFunc->hostReturnInMemory) || (retInMemPointer==NULL && !sysFunc->hostReturnInMemory) );
  692. context->isCallingSystemFunction = true;
  693. switch( callConv )
  694. {
  695. case ICC_CDECL:
  696. case ICC_CDECL_RETURNINMEM:
  697. case ICC_STDCALL:
  698. case ICC_STDCALL_RETURNINMEM:
  699. retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retInMemPointer );
  700. break;
  701. case ICC_THISCALL:
  702. case ICC_THISCALL_RETURNINMEM:
  703. retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer );
  704. break;
  705. case ICC_VIRTUAL_THISCALL:
  706. case ICC_VIRTUAL_THISCALL_RETURNINMEM:
  707. // Get virtual function table from the object pointer
  708. vftable = *(asDWORD**)obj;
  709. retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retInMemPointer );
  710. break;
  711. case ICC_CDECL_OBJLAST:
  712. case ICC_CDECL_OBJLAST_RETURNINMEM:
  713. retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer );
  714. break;
  715. case ICC_CDECL_OBJFIRST:
  716. case ICC_CDECL_OBJFIRST_RETURNINMEM:
  717. retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer );
  718. break;
  719. default:
  720. context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
  721. }
  722. context->isCallingSystemFunction = false;
  723. #ifdef COMPLEX_OBJS_PASSED_BY_REF
  724. if( sysFunc->takesObjByVal )
  725. {
  726. // Need to free the complex objects passed by value
  727. args = context->regs.stackPointer;
  728. if( callConv >= ICC_THISCALL && !objectPointer )
  729. args++;
  730. int spos = 0;
  731. for( int n = 0; n < (int)descr->parameterTypes.GetLength(); n++ )
  732. {
  733. if( descr->parameterTypes[n].IsObject() &&
  734. !descr->parameterTypes[n].IsReference() &&
  735. (descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) )
  736. {
  737. void *obj = (void*)args[spos++];
  738. asSTypeBehaviour *beh = &descr->parameterTypes[n].GetObjectType()->beh;
  739. if( beh->destruct )
  740. {
  741. engine->CallObjectMethod(obj, beh->destruct);
  742. }
  743. engine->CallFree(obj);
  744. }
  745. else
  746. {
  747. spos += descr->parameterTypes[n].GetSizeOnStackDWords();
  748. }
  749. if( IsVariableArgument(descr->parameterTypes[n]) )
  750. {
  751. // account for the implicit TypeID
  752. ++spos;
  753. }
  754. }
  755. }
  756. #endif
  757. // Store the returned value in our stack
  758. if( descr->returnType.IsObject() && !descr->returnType.IsReference() )
  759. {
  760. if( descr->returnType.IsObjectHandle() )
  761. {
  762. // returning an object handle
  763. context->regs.objectRegister = (void*)(asDWORD)retQW;
  764. if( sysFunc->returnAutoHandle && context->regs.objectRegister )
  765. {
  766. engine->CallObjectMethod(context->regs.objectRegister, descr->returnType.GetObjectType()->beh.addref);
  767. }
  768. }
  769. else
  770. {
  771. // returning an object
  772. if( !sysFunc->hostReturnInMemory )
  773. {
  774. // In this case, AngelScript wants an object pointer back, but the host system
  775. // didn't use 'return in memory', so its results were passed back by the return register.
  776. // We have have to take the results of the return register and store them IN the pointer for the object.
  777. // The data for the object could fit into a register; we need to copy that data to the object pointer's
  778. // memory.
  779. assert( retInMemPointer == NULL );
  780. assert( retObjPointer != NULL );
  781. // Copy the returned value to the pointer sent by the script engine
  782. if( sysFunc->hostReturnSize == 1 )
  783. {
  784. *(asDWORD*)retObjPointer = (asDWORD)retQW;
  785. }
  786. else
  787. {
  788. *(asQWORD*)retObjPointer = retQW;
  789. }
  790. }
  791. else
  792. {
  793. // In this case, AngelScript wants an object pointer back, and the host system
  794. // used 'return in memory'. So its results were already passed back in memory, and
  795. // stored in the object pointer.
  796. assert( retInMemPointer != NULL );
  797. assert( retObjPointer != NULL );
  798. }
  799. // store the return results into the object register
  800. context->regs.objectRegister = retObjPointer;
  801. }
  802. }
  803. else
  804. {
  805. // Store value in returnVal register
  806. if( sysFunc->hostReturnFloat )
  807. {
  808. // floating pointer primitives
  809. if( sysFunc->hostReturnSize == 1 )
  810. {
  811. // single float
  812. *(asDWORD*)&context->regs.valueRegister = GetReturnedFloat();
  813. }
  814. else
  815. {
  816. // double float
  817. context->regs.valueRegister = GetReturnedDouble();
  818. }
  819. }
  820. else if( sysFunc->hostReturnSize == 1 )
  821. {
  822. // <=32 bit primitives
  823. // due to endian issues we need to handle return values, that are
  824. // less than a DWORD (32 bits) in size, special
  825. int numBytes = descr->returnType.GetSizeInMemoryBytes();
  826. if( descr->returnType.IsReference() ) numBytes = 4;
  827. switch( numBytes )
  828. {
  829. case 1:
  830. {
  831. // 8 bits
  832. asBYTE *val = (asBYTE*)ARG_DW(context->regs.valueRegister);
  833. val[0] = (asBYTE)retQW;
  834. val[1] = 0;
  835. val[2] = 0;
  836. val[3] = 0;
  837. val[4] = 0;
  838. val[5] = 0;
  839. val[6] = 0;
  840. val[7] = 0;
  841. }
  842. break;
  843. case 2:
  844. {
  845. // 16 bits
  846. asWORD *val = (asWORD*)ARG_DW(context->regs.valueRegister);
  847. val[0] = (asWORD)retQW;
  848. val[1] = 0;
  849. val[2] = 0;
  850. val[3] = 0;
  851. }
  852. break;
  853. default:
  854. {
  855. // 32 bits
  856. asDWORD *val = (asDWORD*)ARG_DW(context->regs.valueRegister);
  857. val[0] = (asDWORD)retQW;
  858. val[1] = 0;
  859. }
  860. break;
  861. }
  862. }
  863. else
  864. {
  865. // 64 bit primitive
  866. context->regs.valueRegister = retQW;
  867. }
  868. }
  869. if( sysFunc->hasAutoHandles )
  870. {
  871. args = context->regs.stackPointer;
  872. if( callConv >= ICC_THISCALL && !objectPointer )
  873. {
  874. args++;
  875. }
  876. int spos = 0;
  877. for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
  878. {
  879. if( sysFunc->paramAutoHandles[n] && args[spos] )
  880. {
  881. // Call the release method on the type
  882. engine->CallObjectMethod((void*)args[spos], descr->parameterTypes[n].GetObjectType()->beh.release);
  883. args[spos] = 0;
  884. }
  885. if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
  886. {
  887. spos++;
  888. }
  889. else
  890. {
  891. spos += descr->parameterTypes[n].GetSizeOnStackDWords();
  892. }
  893. if( IsVariableArgument( descr->parameterTypes[n] ) )
  894. {
  895. // account for the implicit TypeID
  896. ++spos;
  897. }
  898. }
  899. }
  900. return popSize;
  901. }
  902. END_AS_NAMESPACE
  903. #endif // AS_PPC_64
  904. #endif // AS_MAX_PORTABILITY