as_callfunc_x86.cpp 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289
  1. /*
  2. AngelCode Scripting Library
  3. Copyright (c) 2003-2012 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. [email protected]
  22. */
  23. //
  24. // as_callfunc_x86.cpp
  25. //
  26. // These functions handle the actual calling of system functions
  27. //
  28. #include "as_config.h"
  29. #ifndef AS_MAX_PORTABILITY
  30. #ifdef AS_X86
  31. #include "as_callfunc.h"
  32. #include "as_scriptengine.h"
  33. #include "as_texts.h"
  34. #include "as_tokendef.h"
  35. BEGIN_AS_NAMESPACE
  36. //
  37. // With some compile level optimizations the functions don't clear the FPU
  38. // stack themselves. So we have to do it as part of calling the native functions,
  39. // as the compiler will not be able to predict when it is supposed to do it by
  40. // itself due to the dynamic nature of scripts
  41. //
  42. // - fninit clears the FPU stack and the FPU control word
  43. // - emms only clears the FPU stack, while preserving the FPU control word
  44. //
  45. // By default I use fninit as it seems to be what works for most people,
  46. // but some may find it necessary to define this as emms instead.
  47. //
  48. // TODO: Figure out when one or the other must be used, and a way to
  49. // configure this automatically in as_config.h
  50. //
  51. #ifndef CLEAR_FPU_STACK
  52. #define CLEAR_FPU_STACK fninit
  53. #endif
  54. // These macros are just to allow me to use the above macro in the GNUC style inline assembly
  55. #define _S(x) _TOSTRING(x)
  56. #define _TOSTRING(x) #x
  57. // Prototypes
  58. asQWORD CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
  59. asQWORD CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
  60. asQWORD CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
  61. asQWORD CallCDeclFunctionRetByRef(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
  62. asQWORD CallCDeclFunctionRetByRefObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
  63. asQWORD CallCDeclFunctionRetByRefObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
  64. asQWORD CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
  65. asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
  66. asQWORD CallThisCallFunctionRetByRef(const void *, const asDWORD *, int, asFUNCTION_t, void *retPtr);
  67. asDWORD GetReturnedFloat();
  68. asQWORD GetReturnedDouble();
  69. asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/)
  70. {
  71. asCScriptEngine *engine = context->m_engine;
  72. asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
  73. asQWORD retQW = 0;
  74. // Prepare the parameters
  75. int paramSize = sysFunc->paramSize;
  76. asDWORD paramBuffer[64];
  77. if( sysFunc->takesObjByVal )
  78. {
  79. paramSize = 0;
  80. int spos = 0;
  81. int dpos = 1;
  82. for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
  83. {
  84. if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
  85. {
  86. #ifdef COMPLEX_OBJS_PASSED_BY_REF
  87. if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK )
  88. {
  89. paramBuffer[dpos++] = args[spos++];
  90. paramSize++;
  91. }
  92. else
  93. #endif
  94. {
  95. // Copy the object's memory to the buffer
  96. memcpy(&paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes());
  97. // Delete the original memory
  98. engine->CallFree(*(char**)(args+spos));
  99. spos++;
  100. dpos += descr->parameterTypes[n].GetSizeInMemoryDWords();
  101. paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords();
  102. }
  103. }
  104. else
  105. {
  106. // Copy the value directly
  107. paramBuffer[dpos++] = args[spos++];
  108. if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
  109. paramBuffer[dpos++] = args[spos++];
  110. paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
  111. }
  112. }
  113. // Keep a free location at the beginning
  114. args = &paramBuffer[1];
  115. }
  116. // Make the actual call
  117. asFUNCTION_t func = sysFunc->func;
  118. int callConv = sysFunc->callConv;
  119. if( sysFunc->hostReturnInMemory )
  120. callConv++;
  121. switch( callConv )
  122. {
  123. case ICC_CDECL:
  124. retQW = CallCDeclFunction(args, paramSize<<2, func);
  125. break;
  126. case ICC_CDECL_RETURNINMEM:
  127. retQW = CallCDeclFunctionRetByRef(args, paramSize<<2, func, retPointer);
  128. break;
  129. case ICC_STDCALL:
  130. retQW = CallSTDCallFunction(args, paramSize<<2, func);
  131. break;
  132. case ICC_STDCALL_RETURNINMEM:
  133. // Push the return pointer on the stack
  134. paramSize++;
  135. args--;
  136. *(asPWORD*)args = (size_t)retPointer;
  137. retQW = CallSTDCallFunction(args, paramSize<<2, func);
  138. break;
  139. case ICC_THISCALL:
  140. retQW = CallThisCallFunction(obj, args, paramSize<<2, func);
  141. break;
  142. case ICC_THISCALL_RETURNINMEM:
  143. retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, func, retPointer);
  144. break;
  145. case ICC_VIRTUAL_THISCALL:
  146. {
  147. // Get virtual function table from the object pointer
  148. asFUNCTION_t *vftable = *(asFUNCTION_t**)obj;
  149. retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2]);
  150. }
  151. break;
  152. case ICC_VIRTUAL_THISCALL_RETURNINMEM:
  153. {
  154. // Get virtual function table from the object pointer
  155. asFUNCTION_t *vftable = *(asFUNCTION_t**)obj;
  156. retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2], retPointer);
  157. }
  158. break;
  159. case ICC_CDECL_OBJLAST:
  160. retQW = CallCDeclFunctionObjLast(obj, args, paramSize<<2, func);
  161. break;
  162. case ICC_CDECL_OBJLAST_RETURNINMEM:
  163. // Call the system object method as a cdecl with the obj ref as the last parameter
  164. retQW = CallCDeclFunctionRetByRefObjLast(obj, args, paramSize<<2, func, retPointer);
  165. break;
  166. case ICC_CDECL_OBJFIRST:
  167. // Call the system object method as a cdecl with the obj ref as the first parameter
  168. retQW = CallCDeclFunctionObjFirst(obj, args, paramSize<<2, func);
  169. break;
  170. case ICC_CDECL_OBJFIRST_RETURNINMEM:
  171. // Call the system object method as a cdecl with the obj ref as the first parameter
  172. retQW = CallCDeclFunctionRetByRefObjFirst(obj, args, paramSize<<2, func, retPointer);
  173. break;
  174. default:
  175. context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
  176. }
  177. // If the return is a float value we need to get the value from the FP register
  178. if( sysFunc->hostReturnFloat )
  179. {
  180. if( sysFunc->hostReturnSize == 1 )
  181. *(asDWORD*)&retQW = GetReturnedFloat();
  182. else
  183. retQW = GetReturnedDouble();
  184. }
  185. return retQW;
  186. }
  187. // On GCC we need to prevent the compiler from inlining these assembler routines when
  188. // optimizing for speed (-O3), as the loop labels get duplicated which cause compile errors.
  189. #ifdef __GNUC__
  190. #define NOINLINE __attribute ((__noinline__))
  191. #else
  192. #define NOINLINE
  193. #endif
  194. asQWORD NOINLINE CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
  195. {
  196. volatile asQWORD retQW;
  197. #if defined ASM_INTEL
  198. // Copy the data to the real stack. If we fail to do
  199. // this we may run into trouble in case of exceptions.
  200. __asm
  201. {
  202. // We must save registers that are used
  203. push ecx
  204. // Clear the FPU stack, in case the called function doesn't do it by itself
  205. CLEAR_FPU_STACK
  206. // Copy arguments from script
  207. // stack to application stack
  208. mov ecx, paramSize
  209. mov eax, args
  210. add eax, ecx
  211. cmp ecx, 0
  212. je endcopy
  213. copyloop:
  214. sub eax, 4
  215. push dword ptr [eax]
  216. sub ecx, 4
  217. jne copyloop
  218. endcopy:
  219. // Call function
  220. call [func]
  221. // Pop arguments from stack
  222. add esp, paramSize
  223. // Copy return value from EAX:EDX
  224. lea ecx, retQW
  225. mov [ecx], eax
  226. mov 4[ecx], edx
  227. // Restore registers
  228. pop ecx
  229. }
  230. #elif defined ASM_AT_N_T
  231. // It is not possible to rely on ESP or BSP to refer to variables or arguments on the stack
  232. // depending on compiler settings BSP may not even be used, and the ESP is not always on the
  233. // same offset from the local variables. Because the code adjusts the ESP register it is not
  234. // possible to inform the arguments through symbolic names below.
  235. // It's not also not possible to rely on the memory layout of the function arguments, because
  236. // on some compiler versions and settings the arguments may be copied to local variables with a
  237. // different ordering before they are accessed by the rest of the code.
  238. // I'm copying the arguments into this array where I know the exact memory layout. The address
  239. // of this array will then be passed to the inline asm in the EDX register.
  240. volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func)};
  241. asm __volatile__(
  242. _S(CLEAR_FPU_STACK) "\n"
  243. "pushl %%ebx \n"
  244. "movl %%edx, %%ebx \n"
  245. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  246. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  247. // to calculate how much we will put on the stack during this call.
  248. "movl 4(%%ebx), %%eax \n" // paramSize
  249. "addl $4, %%eax \n" // counting esp that we will push on the stack
  250. "movl %%esp, %%ecx \n"
  251. "subl %%eax, %%ecx \n"
  252. "andl $15, %%ecx \n"
  253. "movl %%esp, %%eax \n"
  254. "subl %%ecx, %%esp \n"
  255. "pushl %%eax \n" // Store the original stack pointer
  256. // Copy all arguments to the stack and call the function
  257. "movl 4(%%ebx), %%ecx \n" // paramSize
  258. "movl 0(%%ebx), %%eax \n" // args
  259. "addl %%ecx, %%eax \n" // push arguments on the stack
  260. "cmp $0, %%ecx \n"
  261. "je endcopy \n"
  262. "copyloop: \n"
  263. "subl $4, %%eax \n"
  264. "pushl (%%eax) \n"
  265. "subl $4, %%ecx \n"
  266. "jne copyloop \n"
  267. "endcopy: \n"
  268. "call *8(%%ebx) \n"
  269. "addl 4(%%ebx), %%esp \n" // pop arguments
  270. // Pop the alignment bytes
  271. "popl %%esp \n"
  272. "popl %%ebx \n"
  273. // Copy EAX:EDX to retQW. As the stack pointer has been
  274. // restored it is now safe to access the local variable
  275. "leal %1, %%ecx \n"
  276. "movl %%eax, 0(%%ecx) \n"
  277. "movl %%edx, 4(%%ecx) \n"
  278. : // output
  279. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  280. : "%eax", "%ecx" // clobber
  281. );
  282. #endif
  283. return retQW;
  284. }
  285. asQWORD NOINLINE CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
  286. {
  287. volatile asQWORD retQW;
  288. #if defined ASM_INTEL
  289. // Copy the data to the real stack. If we fail to do
  290. // this we may run into trouble in case of exceptions.
  291. __asm
  292. {
  293. // We must save registers that are used
  294. push ecx
  295. // Clear the FPU stack, in case the called function doesn't do it by itself
  296. CLEAR_FPU_STACK
  297. // Push the object pointer as the last argument to the function
  298. push obj
  299. // Copy arguments from script
  300. // stack to application stack
  301. mov ecx, paramSize
  302. mov eax, args
  303. add eax, ecx
  304. cmp ecx, 0
  305. je endcopy
  306. copyloop:
  307. sub eax, 4
  308. push dword ptr [eax]
  309. sub ecx, 4
  310. jne copyloop
  311. endcopy:
  312. // Call function
  313. call [func]
  314. // Pop arguments from stack
  315. add esp, paramSize
  316. add esp, 4
  317. // Copy return value from EAX:EDX
  318. lea ecx, retQW
  319. mov [ecx], eax
  320. mov 4[ecx], edx
  321. // Restore registers
  322. pop ecx
  323. }
  324. #elif defined ASM_AT_N_T
  325. volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)};
  326. asm __volatile__ (
  327. _S(CLEAR_FPU_STACK) "\n"
  328. "pushl %%ebx \n"
  329. "movl %%edx, %%ebx \n"
  330. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  331. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  332. // to calculate how much we will put on the stack during this call.
  333. "movl 8(%%ebx), %%eax \n" // paramSize
  334. "addl $8, %%eax \n" // counting esp that we will push on the stack
  335. "movl %%esp, %%ecx \n"
  336. "subl %%eax, %%ecx \n"
  337. "andl $15, %%ecx \n"
  338. "movl %%esp, %%eax \n"
  339. "subl %%ecx, %%esp \n"
  340. "pushl %%eax \n" // Store the original stack pointer
  341. "pushl 0(%%ebx) \n" // obj
  342. "movl 8(%%ebx), %%ecx \n" // paramSize
  343. "movl 4(%%ebx), %%eax \n" // args
  344. "addl %%ecx, %%eax \n" // push arguments on the stack
  345. "cmp $0, %%ecx \n"
  346. "je endcopy8 \n"
  347. "copyloop8: \n"
  348. "subl $4, %%eax \n"
  349. "pushl (%%eax) \n"
  350. "subl $4, %%ecx \n"
  351. "jne copyloop8 \n"
  352. "endcopy8: \n"
  353. "call *12(%%ebx) \n"
  354. "addl 8(%%ebx), %%esp \n" // pop arguments
  355. "addl $4, %%esp \n" // pop obj
  356. // Pop the alignment bytes
  357. "popl %%esp \n"
  358. "popl %%ebx \n"
  359. // Copy EAX:EDX to retQW. As the stack pointer has been
  360. // restored it is now safe to access the local variable
  361. "leal %1, %%ecx \n"
  362. "movl %%eax, 0(%%ecx) \n"
  363. "movl %%edx, 4(%%ecx) \n"
  364. : // output
  365. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  366. : "%eax", "%ecx" // clobber
  367. );
  368. #endif
  369. return retQW;
  370. }
  371. asQWORD NOINLINE CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
  372. {
  373. volatile asQWORD retQW;
  374. #if defined ASM_INTEL
  375. // Copy the data to the real stack. If we fail to do
  376. // this we may run into trouble in case of exceptions.
  377. __asm
  378. {
  379. // We must save registers that are used
  380. push ecx
  381. // Clear the FPU stack, in case the called function doesn't do it by itself
  382. CLEAR_FPU_STACK
  383. // Copy arguments from script
  384. // stack to application stack
  385. mov ecx, paramSize
  386. mov eax, args
  387. add eax, ecx
  388. cmp ecx, 0
  389. je endcopy
  390. copyloop:
  391. sub eax, 4
  392. push dword ptr [eax]
  393. sub ecx, 4
  394. jne copyloop
  395. endcopy:
  396. // push object as first parameter
  397. push obj
  398. // Call function
  399. call [func]
  400. // Pop arguments from stack
  401. add esp, paramSize
  402. add esp, 4
  403. // Copy return value from EAX:EDX
  404. lea ecx, retQW
  405. mov [ecx], eax
  406. mov 4[ecx], edx
  407. // Restore registers
  408. pop ecx
  409. }
  410. #elif defined ASM_AT_N_T
  411. volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)};
  412. asm __volatile__ (
  413. _S(CLEAR_FPU_STACK) "\n"
  414. "pushl %%ebx \n"
  415. "movl %%edx, %%ebx \n"
  416. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  417. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  418. // to calculate how much we will put on the stack during this call.
  419. "movl 8(%%ebx), %%eax \n" // paramSize
  420. "addl $8, %%eax \n" // counting esp that we will push on the stack
  421. "movl %%esp, %%ecx \n"
  422. "subl %%eax, %%ecx \n"
  423. "andl $15, %%ecx \n"
  424. "movl %%esp, %%eax \n"
  425. "subl %%ecx, %%esp \n"
  426. "pushl %%eax \n" // Store the original stack pointer
  427. "movl 8(%%ebx), %%ecx \n" // paramSize
  428. "movl 4(%%ebx), %%eax \n" // args
  429. "addl %%ecx, %%eax \n" // push arguments on the stack
  430. "cmp $0, %%ecx \n"
  431. "je endcopy6 \n"
  432. "copyloop6: \n"
  433. "subl $4, %%eax \n"
  434. "pushl (%%eax) \n"
  435. "subl $4, %%ecx \n"
  436. "jne copyloop6 \n"
  437. "endcopy6: \n"
  438. "pushl 0(%%ebx) \n" // push obj
  439. "call *12(%%ebx) \n"
  440. "addl 8(%%ebx), %%esp \n" // pop arguments
  441. "addl $4, %%esp \n" // pop obj
  442. // Pop the alignment bytes
  443. "popl %%esp \n"
  444. "popl %%ebx \n"
  445. // Copy EAX:EDX to retQW. As the stack pointer has been
  446. // restored it is now safe to access the local variable
  447. "leal %1, %%ecx \n"
  448. "movl %%eax, 0(%%ecx) \n"
  449. "movl %%edx, 4(%%ecx) \n"
  450. : // output
  451. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  452. : "%eax", "%ecx" // clobber
  453. );
  454. #endif
  455. return retQW;
  456. }
  457. asQWORD NOINLINE CallCDeclFunctionRetByRefObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
  458. {
  459. volatile asQWORD retQW;
  460. #if defined ASM_INTEL
  461. // Copy the data to the real stack. If we fail to do
  462. // this we may run into trouble in case of exceptions.
  463. __asm
  464. {
  465. // We must save registers that are used
  466. push ecx
  467. // Clear the FPU stack, in case the called function doesn't do it by itself
  468. CLEAR_FPU_STACK
  469. // Copy arguments from script
  470. // stack to application stack
  471. mov ecx, paramSize
  472. mov eax, args
  473. add eax, ecx
  474. cmp ecx, 0
  475. je endcopy
  476. copyloop:
  477. sub eax, 4
  478. push dword ptr [eax]
  479. sub ecx, 4
  480. jne copyloop
  481. endcopy:
  482. // Push the object pointer
  483. push obj
  484. // Push the return pointer
  485. push retPtr;
  486. // Call function
  487. call [func]
  488. // Pop arguments from stack
  489. add esp, paramSize
  490. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  491. // Pop the return pointer
  492. add esp, 8
  493. #else
  494. add esp, 4
  495. #endif
  496. // Copy return value from EAX:EDX
  497. lea ecx, retQW
  498. mov [ecx], eax
  499. mov 4[ecx], edx
  500. // Restore registers
  501. pop ecx
  502. }
  503. #elif defined ASM_AT_N_T
  504. volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)};
  505. asm __volatile__ (
  506. _S(CLEAR_FPU_STACK) "\n"
  507. "pushl %%ebx \n"
  508. "movl %%edx, %%ebx \n"
  509. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  510. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  511. // to calculate how much we will put on the stack during this call.
  512. "movl 8(%%ebx), %%eax \n" // paramSize
  513. "addl $12, %%eax \n" // counting esp that we will push on the stack
  514. "movl %%esp, %%ecx \n"
  515. "subl %%eax, %%ecx \n"
  516. "andl $15, %%ecx \n"
  517. "movl %%esp, %%eax \n"
  518. "subl %%ecx, %%esp \n"
  519. "pushl %%eax \n" // Store the original stack pointer
  520. "movl 8(%%ebx), %%ecx \n" // paramSize
  521. "movl 4(%%ebx), %%eax \n" // args
  522. "addl %%ecx, %%eax \n" // push arguments on the stack
  523. "cmp $0, %%ecx \n"
  524. "je endcopy5 \n"
  525. "copyloop5: \n"
  526. "subl $4, %%eax \n"
  527. "pushl (%%eax) \n"
  528. "subl $4, %%ecx \n"
  529. "jne copyloop5 \n"
  530. "endcopy5: \n"
  531. "pushl 0(%%ebx) \n" // push object first
  532. "pushl 16(%%ebx) \n" // retPtr
  533. "call *12(%%ebx) \n" // func
  534. "addl 8(%%ebx), %%esp \n" // pop arguments
  535. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  536. "addl $8, %%esp \n" // Pop the return pointer and object pointer
  537. #else
  538. "addl $4, %%esp \n" // Pop the object pointer
  539. #endif
  540. // Pop the alignment bytes
  541. "popl %%esp \n"
  542. "popl %%ebx \n"
  543. // Copy EAX:EDX to retQW. As the stack pointer has been
  544. // restored it is now safe to access the local variable
  545. "leal %1, %%ecx \n"
  546. "movl %%eax, 0(%%ecx) \n"
  547. "movl %%edx, 4(%%ecx) \n"
  548. : // output
  549. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  550. : "%eax", "%ecx" // clobber
  551. );
  552. #endif
  553. return retQW;
  554. }
  555. asQWORD NOINLINE CallCDeclFunctionRetByRef(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
  556. {
  557. volatile asQWORD retQW;
  558. #if defined ASM_INTEL
  559. // Copy the data to the real stack. If we fail to do
  560. // this we may run into trouble in case of exceptions.
  561. __asm
  562. {
  563. // We must save registers that are used
  564. push ecx
  565. // Clear the FPU stack, in case the called function doesn't do it by itself
  566. CLEAR_FPU_STACK
  567. // Copy arguments from script
  568. // stack to application stack
  569. mov ecx, paramSize
  570. mov eax, args
  571. add eax, ecx
  572. cmp ecx, 0
  573. je endcopy
  574. copyloop:
  575. sub eax, 4
  576. push dword ptr [eax]
  577. sub ecx, 4
  578. jne copyloop
  579. endcopy:
  580. // Push the return pointer
  581. push retPtr;
  582. // Call function
  583. call [func]
  584. // Pop arguments from stack
  585. add esp, paramSize
  586. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  587. // Pop the return pointer
  588. add esp, 4
  589. #endif
  590. // Copy return value from EAX:EDX
  591. lea ecx, retQW
  592. mov [ecx], eax
  593. mov 4[ecx], edx
  594. // Restore registers
  595. pop ecx
  596. // return value in EAX or EAX:EDX
  597. }
  598. #elif defined ASM_AT_N_T
  599. volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)};
  600. asm __volatile__ (
  601. _S(CLEAR_FPU_STACK) "\n"
  602. "pushl %%ebx \n"
  603. "movl %%edx, %%ebx \n"
  604. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  605. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  606. // to calculate how much we will put on the stack during this call.
  607. "movl 4(%%ebx), %%eax \n" // paramSize
  608. "addl $8, %%eax \n" // counting esp that we will push on the stack
  609. "movl %%esp, %%ecx \n"
  610. "subl %%eax, %%ecx \n"
  611. "andl $15, %%ecx \n"
  612. "movl %%esp, %%eax \n"
  613. "subl %%ecx, %%esp \n"
  614. "pushl %%eax \n" // Store the original stack pointer
  615. "movl 4(%%ebx), %%ecx \n" // paramSize
  616. "movl 0(%%ebx), %%eax \n" // args
  617. "addl %%ecx, %%eax \n" // push arguments on the stack
  618. "cmp $0, %%ecx \n"
  619. "je endcopy7 \n"
  620. "copyloop7: \n"
  621. "subl $4, %%eax \n"
  622. "pushl (%%eax) \n"
  623. "subl $4, %%ecx \n"
  624. "jne copyloop7 \n"
  625. "endcopy7: \n"
  626. "pushl 12(%%ebx) \n" // retPtr
  627. "call *8(%%ebx) \n" // func
  628. "addl 4(%%ebx), %%esp \n" // pop arguments
  629. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  630. "addl $4, %%esp \n" // Pop the return pointer
  631. #endif
  632. // Pop the alignment bytes
  633. "popl %%esp \n"
  634. "popl %%ebx \n"
  635. // Copy EAX:EDX to retQW. As the stack pointer has been
  636. // restored it is now safe to access the local variable
  637. "leal %1, %%ecx \n"
  638. "movl %%eax, 0(%%ecx) \n"
  639. "movl %%edx, 4(%%ecx) \n"
  640. : // output
  641. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  642. : "%eax", "%ecx" // clobber
  643. );
  644. #endif
  645. return retQW;
  646. }
  647. asQWORD NOINLINE CallCDeclFunctionRetByRefObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
  648. {
  649. volatile asQWORD retQW;
  650. #if defined ASM_INTEL
  651. // Copy the data to the real stack. If we fail to do
  652. // this we may run into trouble in case of exceptions.
  653. __asm
  654. {
  655. // We must save registers that are used
  656. push ecx
  657. // Clear the FPU stack, in case the called function doesn't do it by itself
  658. CLEAR_FPU_STACK
  659. push obj
  660. // Copy arguments from script
  661. // stack to application stack
  662. mov ecx, paramSize
  663. mov eax, args
  664. add eax, ecx
  665. cmp ecx, 0
  666. je endcopy
  667. copyloop:
  668. sub eax, 4
  669. push dword ptr [eax]
  670. sub ecx, 4
  671. jne copyloop
  672. endcopy:
  673. // Push the return pointer
  674. push retPtr;
  675. // Call function
  676. call [func]
  677. // Pop arguments from stack
  678. add esp, paramSize
  679. add esp, 4
  680. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  681. // Pop the return pointer
  682. add esp, 4
  683. #endif
  684. // Copy return value from EAX:EDX
  685. lea ecx, retQW
  686. mov [ecx], eax
  687. mov 4[ecx], edx
  688. // Restore registers
  689. pop ecx
  690. }
  691. #elif defined ASM_AT_N_T
  692. volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)};
  693. asm __volatile__ (
  694. _S(CLEAR_FPU_STACK) "\n"
  695. "pushl %%ebx \n"
  696. "movl %%edx, %%ebx \n"
  697. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  698. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  699. // to calculate how much we will put on the stack during this call.
  700. "movl 8(%%ebx), %%eax \n" // paramSize
  701. "addl $12, %%eax \n" // counting esp that we will push on the stack
  702. "movl %%esp, %%ecx \n"
  703. "subl %%eax, %%ecx \n"
  704. "andl $15, %%ecx \n"
  705. "movl %%esp, %%eax \n"
  706. "subl %%ecx, %%esp \n"
  707. "pushl %%eax \n" // Store the original stack pointer
  708. "pushl 0(%%ebx) \n" // obj
  709. "movl 8(%%ebx), %%ecx \n" // paramSize
  710. "movl 4(%%ebx), %%eax \n" // args
  711. "addl %%ecx, %%eax \n" // push arguments on the stack
  712. "cmp $0, %%ecx \n"
  713. "je endcopy4 \n"
  714. "copyloop4: \n"
  715. "subl $4, %%eax \n"
  716. "pushl (%%eax) \n"
  717. "subl $4, %%ecx \n"
  718. "jne copyloop4 \n"
  719. "endcopy4: \n"
  720. "pushl 16(%%ebx) \n" // retPtr
  721. "call *12(%%ebx) \n" // func
  722. "addl 8(%%ebx), %%esp \n" // pop arguments
  723. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  724. "addl $8, %%esp \n" // Pop the return pointer and object pointer
  725. #else
  726. "addl $4, %%esp \n" // Pop the object pointer
  727. #endif
  728. // Pop the alignment bytes
  729. "popl %%esp \n"
  730. "popl %%ebx \n"
  731. // Copy EAX:EDX to retQW. As the stack pointer has been
  732. // restored it is now safe to access the local variable
  733. "leal %1, %%ecx \n"
  734. "movl %%eax, 0(%%ecx) \n"
  735. "movl %%edx, 4(%%ecx) \n"
  736. : // output
  737. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  738. : "%eax", "%ecx" // clobber
  739. );
  740. #endif
  741. return retQW;
  742. }
  743. asQWORD NOINLINE CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
  744. {
  745. volatile asQWORD retQW;
  746. #if defined ASM_INTEL
  747. // Copy the data to the real stack. If we fail to do
  748. // this we may run into trouble in case of exceptions.
  749. __asm
  750. {
  751. // We must save registers that are used
  752. push ecx
  753. // Clear the FPU stack, in case the called function doesn't do it by itself
  754. CLEAR_FPU_STACK
  755. // Copy arguments from script
  756. // stack to application stack
  757. mov ecx, paramSize
  758. mov eax, args
  759. add eax, ecx
  760. cmp ecx, 0
  761. je endcopy
  762. copyloop:
  763. sub eax, 4
  764. push dword ptr [eax]
  765. sub ecx, 4
  766. jne copyloop
  767. endcopy:
  768. // Call function
  769. call [func]
  770. // The callee already removed parameters from the stack
  771. // Copy return value from EAX:EDX
  772. lea ecx, retQW
  773. mov [ecx], eax
  774. mov 4[ecx], edx
  775. // Restore registers
  776. pop ecx
  777. }
  778. #elif defined ASM_AT_N_T
  779. volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func)};
  780. asm __volatile__ (
  781. _S(CLEAR_FPU_STACK) "\n"
  782. "pushl %%ebx \n"
  783. "movl %%edx, %%ebx \n"
  784. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  785. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  786. // to calculate how much we will put on the stack during this call.
  787. "movl 4(%%ebx), %%eax \n" // paramSize
  788. "addl $4, %%eax \n" // counting esp that we will push on the stack
  789. "movl %%esp, %%ecx \n"
  790. "subl %%eax, %%ecx \n"
  791. "andl $15, %%ecx \n"
  792. "movl %%esp, %%eax \n"
  793. "subl %%ecx, %%esp \n"
  794. "pushl %%eax \n" // Store the original stack pointer
  795. "movl 4(%%ebx), %%ecx \n" // paramSize
  796. "movl 0(%%ebx), %%eax \n" // args
  797. "addl %%ecx, %%eax \n" // push arguments on the stack
  798. "cmp $0, %%ecx \n"
  799. "je endcopy2 \n"
  800. "copyloop2: \n"
  801. "subl $4, %%eax \n"
  802. "pushl (%%eax) \n"
  803. "subl $4, %%ecx \n"
  804. "jne copyloop2 \n"
  805. "endcopy2: \n"
  806. "call *8(%%ebx) \n" // callee pops the arguments
  807. // Pop the alignment bytes
  808. "popl %%esp \n"
  809. "popl %%ebx \n"
  810. // Copy EAX:EDX to retQW. As the stack pointer has been
  811. // restored it is now safe to access the local variable
  812. "leal %1, %%ecx \n"
  813. "movl %%eax, 0(%%ecx) \n"
  814. "movl %%edx, 4(%%ecx) \n"
  815. : // output
  816. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  817. : "%eax", "%ecx" // clobber
  818. );
  819. #endif
  820. return retQW;
  821. }
  822. asQWORD NOINLINE CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
  823. {
  824. volatile asQWORD retQW;
  825. #if defined ASM_INTEL
  826. // Copy the data to the real stack. If we fail to do
  827. // this we may run into trouble in case of exceptions.
  828. __asm
  829. {
  830. // We must save registers that are used
  831. push ecx
  832. // Clear the FPU stack, in case the called function doesn't do it by itself
  833. CLEAR_FPU_STACK
  834. // Copy arguments from script
  835. // stack to application stack
  836. mov ecx, paramSize
  837. mov eax, args
  838. add eax, ecx
  839. cmp ecx, 0
  840. je endcopy
  841. copyloop:
  842. sub eax, 4
  843. push dword ptr [eax]
  844. sub ecx, 4
  845. jne copyloop
  846. endcopy:
  847. #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
  848. // Push the object pointer on the stack
  849. push obj
  850. #else
  851. // Move object pointer to ECX
  852. mov ecx, obj
  853. #endif
  854. // Call function
  855. call [func]
  856. #ifndef THISCALL_CALLEE_POPS_ARGUMENTS
  857. // Pop arguments
  858. add esp, paramSize
  859. #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
  860. // Pop object pointer
  861. add esp, 4
  862. #endif
  863. #endif
  864. // Copy return value from EAX:EDX
  865. lea ecx, retQW
  866. mov [ecx], eax
  867. mov 4[ecx], edx
  868. // Restore registers
  869. pop ecx
  870. }
  871. #elif defined ASM_AT_N_T
  872. volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)};
  873. asm __volatile__ (
  874. _S(CLEAR_FPU_STACK) "\n"
  875. "pushl %%ebx \n"
  876. "movl %%edx, %%ebx \n"
  877. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  878. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  879. // to calculate how much we will put on the stack during this call.
  880. "movl 8(%%ebx), %%eax \n" // paramSize
  881. "addl $8, %%eax \n" // counting esp that we will push on the stack
  882. "movl %%esp, %%ecx \n"
  883. "subl %%eax, %%ecx \n"
  884. "andl $15, %%ecx \n"
  885. "movl %%esp, %%eax \n"
  886. "subl %%ecx, %%esp \n"
  887. "pushl %%eax \n" // Store the original stack pointer
  888. "movl 8(%%ebx), %%ecx \n" // paramSize
  889. "movl 4(%%ebx), %%eax \n" // args
  890. "addl %%ecx, %%eax \n" // push all arguments on the stack
  891. "cmp $0, %%ecx \n"
  892. "je endcopy1 \n"
  893. "copyloop1: \n"
  894. "subl $4, %%eax \n"
  895. "pushl (%%eax) \n"
  896. "subl $4, %%ecx \n"
  897. "jne copyloop1 \n"
  898. "endcopy1: \n"
  899. "movl 0(%%ebx), %%ecx \n" // move obj into ECX
  900. "pushl %%ecx \n" // push obj on the stack
  901. "call *12(%%ebx) \n"
  902. "addl 8(%%ebx), %%esp \n" // pop arguments
  903. "addl $4, %%esp \n" // pop obj
  904. // Pop the alignment bytes
  905. "popl %%esp \n"
  906. "popl %%ebx \n"
  907. // Copy EAX:EDX to retQW. As the stack pointer has been
  908. // restored it is now safe to access the local variable
  909. "leal %1, %%ecx \n"
  910. "movl %%eax, 0(%%ecx) \n"
  911. "movl %%edx, 4(%%ecx) \n"
  912. : // output
  913. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  914. : "%eax", "%ecx" // clobber
  915. );
  916. #endif
  917. return retQW;
  918. }
  919. asQWORD NOINLINE CallThisCallFunctionRetByRef(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
  920. {
  921. volatile asQWORD retQW;
  922. #if defined ASM_INTEL
  923. // Copy the data to the real stack. If we fail to do
  924. // this we may run into trouble in case of exceptions.
  925. __asm
  926. {
  927. // We must save registers that are used
  928. push ecx
  929. // Clear the FPU stack, in case the called function doesn't do it by itself
  930. CLEAR_FPU_STACK
  931. // Copy arguments from script
  932. // stack to application stack
  933. mov ecx, paramSize
  934. mov eax, args
  935. add eax, ecx
  936. cmp ecx, 0
  937. je endcopy
  938. copyloop:
  939. sub eax, 4
  940. push dword ptr [eax]
  941. sub ecx, 4
  942. jne copyloop
  943. endcopy:
  944. #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
  945. // Push the object pointer on the stack
  946. push obj
  947. #else
  948. // Move object pointer to ECX
  949. mov ecx, obj
  950. #endif
  951. // Push the return pointer
  952. push retPtr
  953. // Call function
  954. call [func]
  955. #ifndef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER
  956. // Pop the return pointer
  957. add esp, 4
  958. #endif
  959. #ifndef THISCALL_CALLEE_POPS_ARGUMENTS
  960. // Pop arguments
  961. add esp, paramSize
  962. #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
  963. // Pop object pointer
  964. add esp, 4
  965. #endif
  966. #endif
  967. // Copy return value from EAX:EDX
  968. lea ecx, retQW
  969. mov [ecx], eax
  970. mov 4[ecx], edx
  971. // Restore registers
  972. pop ecx
  973. }
  974. #elif defined ASM_AT_N_T
  975. volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)};
  976. asm __volatile__ (
  977. _S(CLEAR_FPU_STACK) "\n"
  978. "pushl %%ebx \n"
  979. "movl %%edx, %%ebx \n"
  980. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  981. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  982. // to calculate how much we will put on the stack during this call.
  983. "movl 8(%%ebx), %%eax \n" // paramSize
  984. "addl $12, %%eax \n" // counting esp that we will push on the stack
  985. "movl %%esp, %%ecx \n"
  986. "subl %%eax, %%ecx \n"
  987. "andl $15, %%ecx \n"
  988. "movl %%esp, %%eax \n"
  989. "subl %%ecx, %%esp \n"
  990. "pushl %%eax \n" // Store the original stack pointer
  991. "movl 8(%%ebx), %%ecx \n" // paramSize
  992. "movl 4(%%ebx), %%eax \n" // args
  993. "addl %%ecx, %%eax \n" // push all arguments to the stack
  994. "cmp $0, %%ecx \n"
  995. "je endcopy3 \n"
  996. "copyloop3: \n"
  997. "subl $4, %%eax \n"
  998. "pushl (%%eax) \n"
  999. "subl $4, %%ecx \n"
  1000. "jne copyloop3 \n"
  1001. "endcopy3: \n"
  1002. "movl 0(%%ebx), %%ecx \n" // move obj into ECX
  1003. "pushl %%ecx \n" // push obj on the stack
  1004. "pushl 16(%%ebx) \n" // push retPtr on the stack
  1005. "call *12(%%ebx) \n"
  1006. #ifndef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER
  1007. "addl $4, %%esp \n" // pop return pointer
  1008. #endif
  1009. "addl 8(%%ebx), %%esp \n" // pop arguments
  1010. "addl $4, %%esp \n" // pop the object pointer
  1011. // the return pointer was popped by the callee
  1012. // Pop the alignment bytes
  1013. "popl %%esp \n"
  1014. "popl %%ebx \n"
  1015. // Copy EAX:EDX to retQW. As the stack pointer has been
  1016. // restored it is now safe to access the local variable
  1017. "leal %1, %%ecx \n"
  1018. "movl %%eax, 0(%%ecx) \n"
  1019. "movl %%edx, 4(%%ecx) \n"
  1020. : // output
  1021. : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
  1022. : "%eax", "%ecx" // clobber
  1023. );
  1024. #endif
  1025. return retQW;
  1026. }
  1027. asDWORD GetReturnedFloat()
  1028. {
  1029. asDWORD f;
  1030. #if defined ASM_INTEL
  1031. // Get the float value from ST0
  1032. __asm fstp dword ptr [f]
  1033. #elif defined ASM_AT_N_T
  1034. asm("fstps %0 \n" : "=m" (f));
  1035. #endif
  1036. return f;
  1037. }
  1038. asQWORD GetReturnedDouble()
  1039. {
  1040. asQWORD d;
  1041. #if defined ASM_INTEL
  1042. // Get the double value from ST0
  1043. __asm fstp qword ptr [d]
  1044. #elif defined ASM_AT_N_T
  1045. asm("fstpl %0 \n" : "=m" (d));
  1046. #endif
  1047. return d;
  1048. }
  1049. END_AS_NAMESPACE
  1050. #endif // AS_X86
  1051. #endif // AS_MAX_PORTABILITY