1 /*
2 AngelCode Scripting Library
3 Copyright (c) 2003-2015 Andreas Jonsson
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any
7 damages arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any
10 purpose, including commercial applications, and to alter it and
11 redistribute it freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you
14 must not claim that you wrote the original software. If you use
15 this software in a product, an acknowledgment in the product
16 documentation would be appreciated but is not required.
17
18 2. Altered source versions must be plainly marked as such, and
19 must not be misrepresented as being the original software.
20
21 3. This notice may not be removed or altered from any source
22 distribution.
23
24 The original version of this library can be located at:
25 http://www.angelcode.com/angelscript/
26
27 Andreas Jonsson
28 andreas@angelcode.com
29 */
30
31
32 //
33 // as_callfunc_sh4.cpp
34 //
35 // These functions handle the actual calling of system functions
36 //
37 // This version is SH4 specific and was originally written
38 // by Fredrik Ehnbom in May, 2004
39 // Later updated for angelscript 2.0.0 by Fredrik Ehnbom in Jan, 2005
40
41 // References:
42 // * http://www.renesas.com/avs/resource/japan/eng/pdf/mpumcu/e602156_sh4.pdf
43 // * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcechp40/html/_callsh4_SH_4_Calling_Standard.asp
44
45
46 #include "as_config.h"
47
48 #ifndef AS_MAX_PORTABILITY
49 #ifdef AS_SH4
50
51 #include "as_callfunc.h"
52 #include "as_scriptengine.h"
53 #include "as_texts.h"
54 #include "as_tokendef.h"
55 #include "as_context.h"
56
57 #include <stdio.h>
58 #include <stdlib.h>
59
60 BEGIN_AS_NAMESPACE
61
62 #define AS_SH4_MAX_ARGS 32
63 // The array used to send values to the correct places.
64 // first 0-4 regular values to load into the r4-r7 registers
65 // then 0-8 float values to load into the fr4-fr11 registers
66 // then (AS_SH4_MAX_ARGS - 12) values to load onto the stack
67 // the +1 is for when CallThis (object methods) is used
68 // extra +1 when returning in memory
69 extern "C" {
70 static asDWORD sh4Args[AS_SH4_MAX_ARGS + 1 + 1];
71 }
72
73 // Loads all data into the correct places and calls the function.
74 // intArgSize is the size in bytes for how much data to put in int registers
75 // floatArgSize is the size in bytes for how much data to put in float registers
76 // stackArgSize is the size in bytes for how much data to put on the callstack
77 extern "C" asQWORD sh4Func(int intArgSize, int floatArgSize, int stackArgSize, asDWORD func);
78
79 asm(""
80 " .align 4\n"
81 " .global _sh4Func\n"
82 "_sh4Func:\n"
83 " mov.l r14,@-r15\n"
84 " mov.l r13,@-r15\n"
85 " mov.l r12,@-r15\n"
86 " sts.l pr,@-r15\n" // must be saved since we call a subroutine
87 " mov r7, r14\n" // func
88 " mov r6, r13\n" // stackArgSize
89 " mov.l r5,@-r15\n" // floatArgSize
90 " mov.l sh4Args,r0\n"
91 " pref @r0\n"
92 " mov r4, r1\n" // intArgsize
93 " mov #33*4,r2\n"
94 " extu.b r2,r2\n" // make unsigned (33*4 = 132 => 128)
95 " mov.l @(r0,r2), r2\n" // r2 has adress for when returning in memory
96 "_sh4f_intarguments:\n" // copy all the int arguments to the respective registers
97 " mov #4*2*2,r3\n" // calculate how many bytes to skip
98 " sub r1,r3\n"
99 " braf r3\n"
100 " add #-4,r1\n" // we are indexing the array backwards, so subtract one (delayed slot)
101 " mov.l @(r0,r1),r7\n" // 4 arguments
102 " add #-4,r1\n"
103 " mov.l @(r0,r1),r6\n" // 3 arguments
104 " add #-4,r1\n"
105 " mov.l @(r0,r1),r5\n" // 2 arguments
106 " add #-4,r1\n"
107 " mov.l @(r0,r1),r4\n" // 1 argument
108 " nop\n"
109 "_sh4f_floatarguments:\n" // copy all the float arguments to the respective registers
110 " add #4*4, r0\n"
111 " mov.l @r15+,r1\n" // floatArgSize
112 " mov #8*2*2,r3\n" // calculate how many bytes to skip
113 " sub r1,r3\n"
114 " braf r3\n"
115 " add #-4,r1\n" // we are indexing the array backwards, so subtract one (delayed slot)
116 " fmov.s @(r0,r1),fr11\n" // 8 arguments
117 " add #-4,r1\n"
118 " fmov.s @(r0,r1),fr10\n" // 7 arguments
119 " add #-4,r1\n"
120 " fmov.s @(r0,r1),fr9\n" // 6 arguments
121 " add #-4,r1\n"
122 " fmov.s @(r0,r1),fr8\n" // 5 arguments
123 " add #-4,r1\n"
124 " fmov.s @(r0,r1),fr7\n" // 4 arguments
125 " add #-4,r1\n"
126 " fmov.s @(r0,r1),fr6\n" // 3 arguments
127 " add #-4,r1\n"
128 " fmov.s @(r0,r1),fr5\n" // 2 arguments
129 " add #-4,r1\n"
130 " fmov.s @(r0,r1),fr4\n" // 1 argument
131 " nop\n"
132 "_sh4f_stackarguments:\n" // copy all the stack argument onto the stack
133 " add #8*4, r0\n"
134 " mov r0, r1\n"
135 " mov #0, r0\n" // init position counter (also used as a 0-check on the line after)
136 " cmp/eq r0, r13\n"
137 " bt _sh4f_functioncall\n" // no arguments to push onto the stack
138 " mov r13, r3\n" // stackArgSize
139 " sub r3,r15\n" // "allocate" space on the stack
140 " shlr2 r3\n" // make into a counter
141 "_sh4f_stackloop:\n"
142 " mov.l @r1+, r12\n"
143 " mov.l r12, @(r0, r15)\n"
144 " add #4, r0\n"
145 " dt r3\n"
146 " bf _sh4f_stackloop\n"
147 "_sh4f_functioncall:\n"
148 " jsr @r14\n" // no arguments
149 " nop\n"
150 " add r13, r15\n" // restore stack position
151 " lds.l @r15+,pr\n"
152 " mov.l @r15+, r12\n"
153 " mov.l @r15+, r13\n"
154 " rts\n"
155 " mov.l @r15+, r14\n" // delayed slot
156 "\n"
157 " .align 4\n"
158 "sh4Args:\n"
159 " .long _sh4Args\n"
160 );
161
162 // puts the arguments in the correct place in the sh4Args-array. See comments above.
163 // This could be done better.
splitArgs(const asDWORD * args,int argNum,int & numRegIntArgs,int & numRegFloatArgs,int & numRestArgs,int hostFlags)164 inline void splitArgs(const asDWORD *args, int argNum, int &numRegIntArgs, int &numRegFloatArgs, int &numRestArgs, int hostFlags) {
165 int i;
166
167 int argBit = 1;
168 for (i = 0; i < argNum; i++) {
169 if (hostFlags & argBit) {
170 if (numRegFloatArgs < 12 - 4) {
171 // put in float register
172 sh4Args[4 + numRegFloatArgs] = args[i];
173 numRegFloatArgs++;
174 } else {
175 // put in stack
176 sh4Args[4 + 8 + numRestArgs] = args[i];
177 numRestArgs++;
178 }
179 } else {
180 if (numRegIntArgs < 8 - 4) {
181 // put in int register
182 sh4Args[numRegIntArgs] = args[i];
183 numRegIntArgs++;
184 } else {
185 // put in stack
186 sh4Args[4 + 8 + numRestArgs] = args[i];
187 numRestArgs++;
188 }
189 }
190 argBit <<= 1;
191 }
192 }
CallCDeclFunction(const asDWORD * args,int argSize,asDWORD func,int flags)193 asQWORD CallCDeclFunction(const asDWORD *args, int argSize, asDWORD func, int flags)
194 {
195 int argNum = argSize >> 2;
196
197 int intArgs = 0;
198 int floatArgs = 0;
199 int restArgs = 0;
200
201 // put the arguments in the correct places in the sh4Args array
202 if (argNum > 0)
203 splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags);
204
205 return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func);
206 }
207
208 // This function is identical to CallCDeclFunction, with the only difference that
209 // the value in the first parameter is the object
CallThisCallFunction(const void * obj,const asDWORD * args,int argSize,asDWORD func,int flags)210 asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags)
211 {
212 int argNum = argSize >> 2;
213
214 int intArgs = 1;
215 int floatArgs = 0;
216 int restArgs = 0;
217
218 sh4Args[0] = (asDWORD) obj;
219
220 // put the arguments in the correct places in the sh4Args array
221 if (argNum >= 1)
222 splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags);
223
224 return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func);
225 }
226 // This function is identical to CallCDeclFunction, with the only difference that
227 // the value in the last parameter is the object
CallThisCallFunction_objLast(const void * obj,const asDWORD * args,int argSize,asDWORD func,int flags)228 asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags)
229 {
230 int argNum = argSize >> 2;
231
232 int intArgs = 0;
233 int floatArgs = 0;
234 int restArgs = 0;
235
236
237 // put the arguments in the correct places in the sh4Args array
238 if (argNum >= 1)
239 splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags);
240
241 if (intArgs < 4) {
242 sh4Args[intArgs] = (asDWORD) obj;
243 intArgs++;
244 } else {
245 sh4Args[4 + 8 + restArgs] = (asDWORD) obj;
246 restArgs++;
247 }
248
249
250 return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func);
251 }
252
GetReturnedFloat()253 asDWORD GetReturnedFloat()
254 {
255 asDWORD f;
256
257 asm("fmov.s fr0, %0\n" : "=m"(f));
258
259 return f;
260 }
261
262 // sizeof(double) == 4 with sh-elf-gcc (3.4.0) -m4
263 // so this isn't really used...
GetReturnedDouble()264 asQWORD GetReturnedDouble()
265 {
266 asQWORD d;
267
268 asm("fmov dr0, %0\n" : "=m"(d));
269
270 return d;
271 }
272
CallSystemFunctionNative(asCContext * context,asCScriptFunction * descr,void * obj,asDWORD * args,void * retPointer,asQWORD &,void *)273 asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/)
274 {
275 // TODO: SH4 does not yet support THISCALL_OBJFIRST/LAST
276
277 asCScriptEngine *engine = context->m_engine;
278 asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
279 int callConv = sysFunc->callConv;
280
281 asQWORD retQW = 0;
282
283 void *func = (void*)sysFunc->func;
284 int paramSize = sysFunc->paramSize;
285 asDWORD *vftable;
286
287 if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() )
288 {
289 sh4Args[AS_SH4_MAX_ARGS+1] = (asDWORD) retPointer;
290 }
291
292 asASSERT(descr->parameterTypes.GetLength() <= 32);
293
294 // mark all float arguments
295 int argBit = 1;
296 int hostFlags = 0;
297 int intArgs = 0;
298 for( asUINT a = 0; a < descr->parameterTypes.GetLength(); a++ ) {
299 if (descr->parameterTypes[a].IsFloatType()) {
300 hostFlags |= argBit;
301 } else intArgs++;
302 argBit <<= 1;
303 }
304
305 asDWORD paramBuffer[64];
306 if( sysFunc->takesObjByVal )
307 {
308 paramSize = 0;
309 int spos = 0;
310 int dpos = 1;
311 for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
312 {
313 if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
314 {
315 #ifdef COMPLEX_OBJS_PASSED_BY_REF
316 if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK )
317 {
318 paramBuffer[dpos++] = args[spos++];
319 paramSize++;
320 }
321 else
322 #endif
323 {
324 // Copy the object's memory to the buffer
325 memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes());
326 // Delete the original memory
327 engine->CallFree(*(char**)(args+spos));
328 spos++;
329 dpos += descr->parameterTypes[n].GetSizeInMemoryDWords();
330 paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords();
331 }
332 }
333 else
334 {
335 // Copy the value directly
336 paramBuffer[dpos++] = args[spos++];
337 if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
338 paramBuffer[dpos++] = args[spos++];
339 paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
340 }
341 }
342 // Keep a free location at the beginning
343 args = ¶mBuffer[1];
344 }
345
346 switch( callConv )
347 {
348 case ICC_CDECL:
349 case ICC_CDECL_RETURNINMEM:
350 case ICC_STDCALL:
351 case ICC_STDCALL_RETURNINMEM:
352 retQW = CallCDeclFunction(args, paramSize<<2, (asDWORD)func, hostFlags);
353 break;
354 case ICC_THISCALL:
355 case ICC_THISCALL_RETURNINMEM:
356 retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags);
357 break;
358 case ICC_VIRTUAL_THISCALL:
359 case ICC_VIRTUAL_THISCALL_RETURNINMEM:
360 // Get virtual function table from the object pointer
361 vftable = *(asDWORD**)obj;
362 retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[asDWORD(func)>>2], hostFlags);
363 break;
364 case ICC_CDECL_OBJLAST:
365 case ICC_CDECL_OBJLAST_RETURNINMEM:
366 retQW = CallThisCallFunction_objLast(obj, args, paramSize<<2, (asDWORD)func, hostFlags);
367 break;
368 case ICC_CDECL_OBJFIRST:
369 case ICC_CDECL_OBJFIRST_RETURNINMEM:
370 retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags);
371 break;
372 default:
373 context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
374 }
375
376 // If the return is a float value we need to get the value from the FP register
377 if( sysFunc->hostReturnFloat )
378 {
379 if( sysFunc->hostReturnSize == 1 )
380 *(asDWORD*)&retQW = GetReturnedFloat();
381 else
382 retQW = GetReturnedDouble();
383 }
384
385 return retQW;
386 }
387
388 END_AS_NAMESPACE
389
390 #endif // AS_SH4
391 #endif // AS_MAX_PORTABILITY
392
393
394