File: as_callfunc_ppc.cpp

package info (click to toggle)
supertuxkart 1.2%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 702,252 kB
  • sloc: cpp: 365,152; xml: 82,321; ansic: 42,245; sh: 1,285; asm: 1,248; python: 833; objc: 452; makefile: 332; awk: 20
file content (674 lines) | stat: -rw-r--r-- 25,685 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2015 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/


//
// as_callfunc_ppc.cpp
//
// These functions handle the actual calling of system functions
//
// This version is PPC specific
//

#include <stdio.h>

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#ifdef AS_PPC

#include "as_callfunc.h"
#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_tokendef.h"
#include "as_context.h"

#include <stdlib.h>

BEGIN_AS_NAMESPACE

// This part was originally written by Pecan Heber, June 2006, for
// use on MacOS X with 32bit PPC processor. He based the code on the
// code in as_callfunc_sh4.cpp

#define AS_PPC_MAX_ARGS 32

// The array used to send values to the correct places.
// Contains a byte of argTypes to indicate the register tYpe to load
// or zero if end of arguments
// The +1 is for when CallThis (object methods) is used
// Extra +1 when returning in memory
// Extra +1 in ppcArgsType to ensure zero end-of-args marker

// TODO: multithread: We need to remove these global variables for thread-safety

enum argTypes { ppcENDARG, ppcINTARG, ppcFLOATARG, ppcDOUBLEARG };
static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1];

// Using extern "C" because we use this symbol name in the assembly code
extern "C"
{
	static asBYTE ppcArgsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1];
}

// NOTE: these values are for PowerPC 32 bit. 
#define PPC_LINKAGE_SIZE  (24)                                   // how big the PPC linkage area is in a stack frame
#define PPC_NUM_REGSTORE  (9)                                    // how many registers of the PPC we need to store/restore for ppcFunc()
#define PPC_REGSTORE_SIZE (4*PPC_NUM_REGSTORE)                   // how many bytes are required for register store/restore
#define EXTRA_STACK_SIZE  (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame
#define PPC_STACK_SIZE(numParams)  (-( ( ((((numParams)<8)?8:(numParams))<<2) + EXTRA_STACK_SIZE + 15 ) & ~15 ))  // calculates the total stack size needed for ppcFunc64, must pad to 16bytes

// Loads all data into the correct places and calls the function.
// ppcArgsType is an array containing a byte type (enum argTypes) for each argument.
// stackArgSize is the size in bytes for how much data to put on the stack frame
extern "C" asQWORD ppcFunc(const asDWORD* argsPtr, int StackArgSize, asDWORD func);

asm(" .text\n"
	" .align 2\n"		  // align the code to 1 << 2 = 4 bytes
	" .globl _ppcFunc\n"
	"_ppcFunc:\n"

	// We're receiving the following parameters
	
	// r3 : argsPtr
	// r4 : StackArgSize
	// r5 : func

	// The following registers are used through out the function
	
	// r31 : the address of the label address, as reference for all other labels
	// r30 : temporary variable
	// r29 : arg list pointer
	// r28 : number of FPR registers used by the parameters
	// r27 : the function pointer that will be called
	// r26 : the location of the parameters for the call
	// r25 : arg type list pointer
	// r24 : temporary variable
	// r23 : number of GPR registers used by the parameters
	// r1  : this is stack pointer
	// r0  : temporary variable
	// f0  : temporary variable

	// We need to store some of the registers for restoral before returning to caller
	
	// lr - always stored in 8(r1) - this is the return address
	// cr - not required to be stored, but if it is, its place is in 4(r1) - this is the condition register 
	// r1 - always stored in 0(r1) - this is the stack pointer
	// r11 
	// r13 to r31 
	// f14 to f31

	// Store register values and setup our stack frame
	" mflr  r0            \n"   // move the return address into r0
	" stw   r0, 8(r1)     \n"   // Store the return address on the stack
	" stmw  r23, -36(r1)  \n"   // Store registers r23 to r31 on the stack
	" stwux r1, r1, r4    \n"   // Increase the stack with the needed space and store the original value in the destination	
	
	// Obtain an address that we'll use as our position of reference when obtaining addresses of other labels
	" bl    address         \n"
	"address:               \n"
	" mflr  r31             \n"

	// initial registers for the function
	" mr    r29, r3       \n"   // (r29) args list
	" mr    r27, r5       \n"   // load the function pointer to call.  func actually holds the pointer to our function
	" addi  r26, r1, 24   \n"   // setup the pointer to the parameter area to the function we're going to call
	" sub   r0, r0, r0    \n"   // zero out r0
	" mr    r23, r0       \n"   // zero out r23, which holds the number of used GPR registers
	" mr    r28, r0       \n"   // zero our r22, which holds the number of used float registers
	
	// load the global ppcArgsType which holds the types of arguments for each argument
	" addis r25, r31, ha16(_ppcArgsType - address) \n" // load the upper 16 bits of the address to r25
	" la    r25, lo16(_ppcArgsType - address)(r25) \n" // load the lower 16 bits of the address to r25
	" subi  r25, r25, 1    \n"   // since we increment r25 on its use, we'll pre-decrement it

	// loop through the arguments
	"ppcNextArg:           \n"
	" addi r25, r25, 1     \n"   // increment r25, our arg type pointer
	// switch based on the current argument type (0:end, 1:int, 2:float 3:double)
	" lbz  r24, 0(r25)     \n"   // load the current argument type (it's a byte)
	" mulli r24, r24, 4    \n"   // our jump table has 4 bytes per case (1 instruction)
	" addis r30, r31, ha16(ppcTypeSwitch - address) \n"   // load the address of the jump table for the switch
	" la    r30, lo16(ppcTypeSwitch - address)(r30) \n"
	
	" add    r0, r30, r24  \n"   // offset by our argument type
	" mtctr  r0            \n"   // load the jump address into CTR
	" bctr                 \n"   // jump into the jump table/switch
	" nop                  \n"
	
	// the jump table/switch based on the current argument type
	"ppcTypeSwitch:        \n"
	" b ppcArgsEnd         \n"
	" b ppcArgIsInteger    \n"
	" b ppcArgIsFloat      \n"
	" b ppcArgIsDouble     \n"
	
	// when we get here we have finished processing all the arguments
	// everything is ready to go to call the function
	"ppcArgsEnd:           \n"
	" mtctr r27            \n"   // the function pointer is stored in r27, load that into CTR
	" bctrl                \n"   // call the function.  We have to do it this way so that the LR gets the proper
	" nop                  \n"   // return value (the next instruction below).  So we have to branch from CTR instead of LR.
	
	// Restore registers and caller's stack frame, then return to caller
	" lwz   r1, 0(r1)      \n"   // restore the caller's stack pointer
	" lwz   r0, 8(r1)      \n"   // load in the caller's LR
	" mtlr  r0             \n"   // restore the caller's LR
	" lmw   r23, -36(r1)   \n"   // restore registers r23 to r31 from the stack
	" blr                  \n"   // return back to the caller
	" nop                  \n"
	
	// Integer argument (GPR register)
	"ppcArgIsInteger:             \n"
	" addis r30, r31, ha16(ppcLoadIntReg - address) \n" // load the address to the jump table for integer registers
	" la    r30, lo16(ppcLoadIntReg - address)(r30) \n"
	" mulli r0, r23, 8            \n"    // each item in the jump table is 2 instructions (8 bytes)
	" add   r0, r0, r30           \n"    // calculate ppcLoadIntReg[numUsedGPRRegs]
	" lwz   r30, 0(r29)           \n"    // load the next argument from the argument list into r30
	" cmpwi r23, 8                \n"    // we can only load GPR3 through GPR10 (8 registers)
	" bgt   ppcLoadIntRegUpd      \n"    // if we're beyond 8 GPR registers, we're in the stack, go there
	" mtctr r0                    \n"    // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers)
	" bctr                        \n"    // load the argument into a GPR register
	" nop                         \n"
	// jump table for GPR registers, for the first 8 GPR arguments
	"ppcLoadIntReg:               \n"
	" mr    r3, r30               \n"    // arg0 (to r3)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r4, r30               \n"    // arg1 (to r4)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r5, r30               \n"    // arg2 (to r5)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r6, r30               \n"    // arg3 (to r6)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r7, r30               \n"    // arg4 (to r7)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r8, r30               \n"    // arg5 (to r8)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r9, r30               \n"    // arg6 (to r9)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r10, r30              \n"    // arg7 (to r10)
	" b     ppcLoadIntRegUpd      \n"
	// all GPR arguments still go on the stack
	"ppcLoadIntRegUpd:            \n"
	" stw   r30, 0(r26)           \n"    // store the argument into the next slot on the stack's argument list
	" addi  r23, r23, 1           \n"    // count a used GPR register
	" addi  r29, r29, 4           \n"    // move to the next argument on the list
	" addi  r26, r26, 4           \n"    // adjust our argument stack pointer for the next
	" b     ppcNextArg            \n"    // next argument

	// single Float argument
	"ppcArgIsFloat:\n"
	" addis r30, r31, ha16(ppcLoadFloatReg - address) \n" // get the base address of the float register jump table
	" la    r30, lo16(ppcLoadFloatReg - address)(r30) \n"
	" mulli r0, r28, 8           \n"     // each jump table entry is 8 bytes
	" add   r0, r0, r30          \n"     // calculate the offset to ppcLoadFloatReg[numUsedFloatReg]
	" lfs   f0, 0(r29)           \n"     // load the next argument as a float into f0
	" cmpwi r28, 13              \n"     // can't load more than 13 float/double registers
	" bgt   ppcLoadFloatRegUpd   \n"     // if we're beyond 13 registers, just fall to inserting into the stack
	" mtctr r0                   \n"     // jump into the float jump table
	" bctr                       \n"
	" nop                        \n"
	// jump table for float registers, for the first 13 float arguments
	"ppcLoadFloatReg:            \n"
	" fmr   f1, f0               \n"     // arg0 (f1)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f2, f0               \n"     // arg1 (f2)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f3, f0               \n"     // arg2 (f3)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f4, f0               \n"     // arg3 (f4)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f5, f0               \n"     // arg4 (f5)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f6, f0               \n"     // arg5 (f6)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f7, f0               \n"     // arg6 (f7)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f8, f0               \n"     // arg7 (f8)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f9, f0               \n"     // arg8 (f9)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f10, f0              \n"     // arg9 (f10)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f11, f0              \n"     // arg10 (f11)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f12, f0              \n"     // arg11 (f12)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f13, f0              \n"     // arg12 (f13)
	" b     ppcLoadFloatRegUpd   \n"
	" nop                        \n"
	// all float arguments still go on the stack
	"ppcLoadFloatRegUpd:         \n"
	" stfs  f0, 0(r26)           \n"     // store, as a single float, f0 (current argument) on to the stack argument list
	" addi  r23, r23, 1          \n"     // a float register eats up a GPR register
	" addi  r28, r28, 1          \n"     // ...and, of course, a float register
	" addi  r29, r29, 4          \n"     // move to the next argument in the list
	" addi  r26, r26, 4          \n"     // move to the next stack slot
	" b     ppcNextArg           \n"     // on to the next argument
	" nop                        \n"
	
	// double Float argument
	"ppcArgIsDouble:             \n"
	" addis r30, r31, ha16(ppcLoadDoubleReg - address) \n" // load the base address of the jump table for double registers
	" la    r30, lo16(ppcLoadDoubleReg - address)(r30) \n"
	" mulli r0, r28, 8           \n"     // each slot of the jump table is 8 bytes
	" add   r0, r0, r30          \n"     // calculate ppcLoadDoubleReg[numUsedFloatReg]
	" lfd   f0, 0(r29)           \n"     // load the next argument, as a double float, into f0
	" cmpwi r28, 13              \n"     // the first 13 floats must go into float registers also
	" bgt   ppcLoadDoubleRegUpd  \n"     // if we're beyond 13, then just put on to the stack
	" mtctr r0                   \n"     // we're under 13, first load our register
	" bctr                       \n"     // jump into the jump table
	" nop                        \n"
	// jump table for float registers, for the first 13 float arguments
	"ppcLoadDoubleReg:           \n"
	" fmr   f1, f0               \n"     // arg0 (f1)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f2, f0               \n"     // arg1 (f2)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f3, f0               \n"     // arg2 (f3)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f4, f0               \n"     // arg3 (f4)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f5, f0               \n"     // arg4 (f5)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f6, f0               \n"     // arg5 (f6)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f7, f0               \n"     // arg6 (f7)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f8, f0               \n"     // arg7 (f8)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f9, f0               \n"     // arg8 (f9)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f10, f0              \n"     // arg9 (f10)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f11, f0              \n"     // arg10 (f11)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f12, f0              \n"     // arg11 (f12)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f13, f0              \n"     // arg12 (f13)
	" b     ppcLoadDoubleRegUpd  \n"
	" nop                        \n"
	// all float arguments still go on the stack
	"ppcLoadDoubleRegUpd: \n"
	" stfd  f0, 0(r26)     \n"    // store f0, as a double, into the argument list on the stack
	" addi  r23, r23, 2   \n"     // a double float eats up two GPRs
	" addi  r28, r28, 1   \n"     // ...and, of course, a float
	" addi  r29, r29, 8   \n"     // increment to our next argument we need to process (8 bytes for the 64bit float)
	" addi  r26, r26, 8   \n"     // increment to the next slot on the argument list on the stack (8 bytes)
	" b     ppcNextArg    \n"     // on to the next argument
	" nop                 \n"
);

asDWORD GetReturnedFloat()
{
	asDWORD f;
	asm(" stfs f1, %0\n" : "=m"(f));
	return f;
}

asQWORD GetReturnedDouble()
{
	asQWORD f;
	asm(" stfd f1, %0\n" : "=m"(f));
	return f;
}

// puts the arguments in the correct place in the stack array. See comments above.
void stackArgs(const asDWORD *args, const asBYTE *argsType, int& numIntArgs, int& numFloatArgs, int& numDoubleArgs)
{
	int i;
	int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2);
	int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs;

	int typeIndex;
	for( i = 0, typeIndex = 0; ; i++, typeIndex++ )
	{
		// store the type
		ppcArgsType[typeOffset++] = argsType[typeIndex];
		if( argsType[typeIndex] == ppcENDARG )
			break;

		switch( argsType[typeIndex] )
		{
		case ppcFLOATARG:
			// stow float
			ppcArgs[argWordPos] = args[i]; // it's just a bit copy
			numFloatArgs++;
			argWordPos++; //add one word
			break;

		case ppcDOUBLEARG:
			// stow double
			memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment
			numDoubleArgs++;
			argWordPos+=2; //add two words
			i++;//doubles take up 2 argument slots
			break;

		case ppcINTARG:
			// stow register
			ppcArgs[argWordPos] = args[i];
			numIntArgs++;
			argWordPos++;
			break;
		}
	}

	// close off the argument list (if we have max args we won't close it off until here)
	ppcArgsType[typeOffset] = ppcENDARG;
}

static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
{
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount   = 1;
	}

	// put the arguments in the correct places in the ppcArgs array
	int numTotalArgs = baseArgCount;
	if( argSize > 0 )
	{
		int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0;
		stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs );
		numTotalArgs = intArgs + floatArgs + 2*doubleArgs; // doubles occupy two slots
	}
	else
	{
		// no arguments, cap the type list
		ppcArgsType[baseArgCount] = ppcENDARG;
	}

	// call the function with the arguments
	return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
}

// This function is identical to CallCDeclFunction, with the only difference that
// the value in the first parameter is the object (unless we are returning in memory)
static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory )
{
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount = 1;
	}

	// the first argument is the 'this' of the object
	ppcArgs[baseArgCount]       = (asDWORD)obj;
	ppcArgsType[baseArgCount++] = ppcINTARG;
	ppcArgsType[baseArgCount]   = ppcENDARG;

	// put the arguments in the correct places in the ppcArgs array
	int numTotalArgs = baseArgCount;
	if( argSize > 0 )
	{
		int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0;
		stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs );
		numTotalArgs = intArgs + floatArgs + 2*doubleArgs; // doubles occupy two slots
	}

	// call the function with the arguments
	return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func);
}

// This function is identical to CallCDeclFunction, with the only difference that
// the value in the last parameter is the object 
// NOTE: on PPC the order for the args is reversed
static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
{
	UNUSED_VAR(argSize);
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount = 1;
	}

	// stack any of the arguments
	int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0;
	stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs );
	int numTotalArgs = intArgs + floatArgs + doubleArgs;

	// can we fit the object in at the end?
	if( numTotalArgs < AS_PPC_MAX_ARGS )
	{
		// put the object pointer at the end
		int argPos = intArgs + floatArgs + (doubleArgs * 2);
		ppcArgs[argPos]             = (asDWORD)obj;
		ppcArgsType[numTotalArgs++] = ppcINTARG;
		ppcArgsType[numTotalArgs]   = ppcENDARG;
	}

	// call the function with the arguments
	return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
}

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/)
{
	// TODO: PPC does not yet support THISCALL_OBJFIRST/LAST

	// use a working array of types, we'll configure the final one in stackArgs
	asBYTE argsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1];
	memset( argsType, 0, sizeof(argsType));

	asCScriptEngine            *engine  = context->m_engine;
	asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;

	asQWORD  retQW           = 0;
	void    *func            = (void*)sysFunc->func;
	int      paramSize       = sysFunc->paramSize;
	asDWORD *vftable         = NULL;
	int      a, s;

	// convert the parameters that are < 4 bytes from little endian to big endian
	int argDwordOffset = 0;
	for( a = 0; a < (int)descr->parameterTypes.GetLength(); a++ )
	{
		int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes();
		if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() )
		{
			argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords();
			continue;
		}

		// flip
		asASSERT( numBytes == 1 || numBytes == 2 );
		switch( numBytes )
		{
		case 1:
			{
				volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]);
				asBYTE t = bPtr[0];
				bPtr[0] = bPtr[3];
				bPtr[3] = t;
				t = bPtr[1];
				bPtr[1] = bPtr[2];
				bPtr[2] = t;
			}
			break;
		case 2:
			{
				volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]);
				asWORD t = wPtr[0];
				wPtr[0] = wPtr[1];
				wPtr[1] = t;
			}
			break;
		}
		argDwordOffset++;
	}

	// mark all float/double/int arguments
	if( !sysFunc->takesObjByVal )
	{
		for( s = 0, a = 0; s < (int)descr->parameterTypes.GetLength(); s++, a++ )
		{
			if( descr->parameterTypes[s].IsFloatType() && !descr->parameterTypes[s].IsReference() )
			{
				argsType[a] = ppcFLOATARG;
			}
			else if( descr->parameterTypes[s].IsDoubleType() && !descr->parameterTypes[s].IsReference() )
			{
				argsType[a] = ppcDOUBLEARG;
			}
			else
			{
				argsType[a] = ppcINTARG;
				if( descr->parameterTypes[s].GetSizeOnStackDWords() == 2 )
				{
					// Add an extra integer argument for the extra size
					a++;
					argsType[a] = ppcINTARG;
				}
			}
		}
	}

	asDWORD paramBuffer[64];
	if( sysFunc->takesObjByVal )
	{
		paramSize = 0;
		int spos = 0;
		int dpos = 1;

        int a = 0;
		for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
		{
			if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
			{
#ifdef COMPLEX_OBJS_PASSED_BY_REF
				if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK )
				{
					argsType[a++] = ppcINTARG;
					paramBuffer[dpos++] = args[spos++];
					paramSize++;
				}
				else
#endif
				{
					// TODO: Probably have to handle asOBJ_APP_FLOAT as a primitive

					// Copy the object's memory to the buffer
					memcpy( &paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() );

					// Delete the original memory
					engine->CallFree(*(char**)(args+spos) );
					spos++;
					asUINT dwords = descr->parameterTypes[n].GetSizeInMemoryDWords();
					dpos += dwords;
					paramSize += dwords;
					for( asUINT i = 0; i < dwords; i++ )
						argsType[a++] = ppcINTARG;
				}
			}
			else
			{
				// Copy the value directly
				paramBuffer[dpos++] = args[spos++];
				if( descr->parameterTypes[n].IsFloatType() && !descr->parameterTypes[n].IsReference() )
					argsType[a++] = ppcFLOATARG;
				else if( descr->parameterTypes[n].IsDoubleType() && !descr->parameterTypes[n].IsReference() )
					argsType[a++] = ppcDOUBLEARG;
				else
					argsType[a++] = ppcINTARG;
				if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
				{
					paramBuffer[dpos++] = args[spos++];
					if( !descr->parameterTypes[n].IsDoubleType() ) // Double already knows it is 2 dwords
						argsType[a++] = ppcINTARG;
				}
				paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
			}
		}

		// Keep a free location at the beginning
		args = &paramBuffer[1];
	}
	
	int callConv = sysFunc->callConv;
	switch( callConv )
	{
	case ICC_CDECL:
	case ICC_CDECL_RETURNINMEM:
	case ICC_STDCALL:
	case ICC_STDCALL_RETURNINMEM:
		retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_THISCALL:
	case ICC_THISCALL_RETURNINMEM:
		retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_VIRTUAL_THISCALL:
	case ICC_VIRTUAL_THISCALL_RETURNINMEM:
		// Get virtual function table from the object pointer
		vftable = *(asDWORD**)obj;
		retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retPointer );
		break;
	case ICC_CDECL_OBJLAST:
	case ICC_CDECL_OBJLAST_RETURNINMEM:
		retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_CDECL_OBJFIRST:
	case ICC_CDECL_OBJFIRST_RETURNINMEM:
		retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	default:
		context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
	}

	// If the return is a float value we need to get the value from the FP register
	if( sysFunc->hostReturnFloat )
	{
		if( sysFunc->hostReturnSize == 1 )
			*(asDWORD*)&retQW = GetReturnedFloat();
		else
			retQW = GetReturnedDouble();
	}

	return retQW;
}

END_AS_NAMESPACE

#endif // AS_PPC
#endif // AS_MAX_PORTABILITY