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(&paramBuffer[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 = &paramBuffer[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