/*

 Package: dyncall
 Library: dyncallback
 File: dyncallback/dyncall_callback_ppc64.S
 Description: Callback Thunk - Implementation for PowerPC 64-bit
 License:

   Copyright (c) 2014-2015 Masanori Mitsugi <mitsugi@linux.vnet.ibm.com>

   Permission to use, copy, modify, and distribute this software for any
   purpose with or without fee is hereby granted, provided that the above
   copyright notice and this permission notice appear in all copies.

   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

#include "../portasm/portasm-ppc64.S"

.text
.align 2

/* struct DCCallback */

#if DC__ABI_PPC64_ELF_V != 2
DCB_THUNK      = 0    /* v1 */
DCB_HANDLER    = 64
DCB_STACKCLEAN = 72
DCB_USERDATA   = 80
#else
DCB_THUNK      = 0    /* v2 */
DCB_HANDLER    = 48
DCB_STACKCLEAN = 56
DCB_USERDATA   = 64
#endif

/* struct DCArgs */

ARGS_IREGS     = 0
ARGS_FREGS     = ARGS_IREGS + 8*8
ARGS_SP        = ARGS_FREGS + 8*13
ARGS_ICNT      = ARGS_SP    + 8
ARGS_FCNT      = ARGS_ICNT  + 4
ARGS_SIZE      = ARGS_FCNT  + 4

/* struct DCValue */

RESULT_SIZE    = 8

/* Stack Offsets */

SP_PREV        = 0
SP_CR          = SP_PREV + 8
SP_LR          = SP_CR   + 8
#if DC__ABI_PPC64_ELF_V != 2
SP_TOC         = 40
SP_PAR         = 48
PAR_SZ         = 64
#else
SP_TOC         = 24
SP_PAR         = 32
PAR_SZ         = 0
#endif
SP_ARGS        = SP_PAR    + PAR_SZ
SP_IREGS       = SP_ARGS   + ARGS_IREGS
SP_FREGS       = SP_ARGS   + ARGS_FREGS
SP_SP          = SP_ARGS   + ARGS_SP
SP_ICNT        = SP_ARGS   + ARGS_ICNT
SP_FCNT        = SP_ARGS   + ARGS_FCNT
SP_RESULT      = SP_ARGS   + ARGS_SIZE
SP_LOCAL       = SP_RESULT + RESULT_SIZE  /* additional locals (reg 30/31) */ 
SP_SIZE        = SP_LOCAL  + 2*8

#define ALIGN(M,X) ( M+(X-1) & (-X) )

FRAMESIZE = ALIGN(SP_SIZE,16)

GLOBAL_C(dcCallbackThunkEntry)
ENTRY_C(dcCallbackThunkEntry)

/* --------------------------------------------------------------------------

Input:
        r1      Stack Pointer            
        r3-r10  Integer Arguments        
        f1-f8   Floating-point Arguments 
        r11     Thunk Pointer            

*/

	mflr    r0
	std     r0,  SP_LR(r1)         /* store return address */
	std     r31, -8(r1)            /* store preserved registers (r31) */
	addi    r12, r1, SP_PAR        /* temporary r12 = parameter area on callers stack frame */
	stdu    r1, -FRAMESIZE(r1)     /* save callers stack pointer and make new stack frame. */

	std     r3, SP_IREGS + 0*8(r1) /* spill 8 integer parameter registers */
	std     r4, SP_IREGS + 1*8(r1)
	std     r5, SP_IREGS + 2*8(r1)
	std     r6, SP_IREGS + 3*8(r1)
	std     r7, SP_IREGS + 4*8(r1)
	std     r8, SP_IREGS + 5*8(r1)
	std     r9, SP_IREGS + 6*8(r1)
	std     r10,SP_IREGS + 7*8(r1)
	stfd    f1, SP_FREGS + 0*8(r1) /* spill 13 float parameter registers */
	stfd    f2, SP_FREGS + 1*8(r1)
	stfd    f3, SP_FREGS + 2*8(r1)
	stfd    f4, SP_FREGS + 3*8(r1)
	stfd    f5, SP_FREGS + 4*8(r1)
	stfd    f6, SP_FREGS + 5*8(r1)
	stfd    f7, SP_FREGS + 6*8(r1)
	stfd    f8, SP_FREGS + 7*8(r1)
	stfd    f9, SP_FREGS + 8*8(r1)
	stfd    f10,SP_FREGS + 9*8(r1)
	stfd    f11,SP_FREGS +10*8(r1)
	stfd    f12,SP_FREGS +11*8(r1)
	stfd    f13,SP_FREGS +12*8(r1)
	                        /* initialize struct DCCallback */
	std     r12,SP_SP(r1)   /* init stack pointer */
	xor     r0, r0, r0      /* init register counters */
	std     r0, SP_ICNT(r1)
	std     r0, SP_FCNT(r1)
	std     r0, SP_RESULT(r1)      /* init result object */ 
	                               /* invoke callback handler */
	mr      r3, r11                /* arg 1: DCCallback* pcb (r11 is thunk pointer) */
	addi    r4, r1, SP_ARGS        /* arg 2: DCArgs* args     */
	addi    r5, r1, SP_RESULT      /* arg 3: DCValue* result  */
	ld      r6, DCB_USERDATA(r11)  /* arg 4: void* userdata   */

	/* branch-and-link to DCCallback.handler */
	ld      r12,  DCB_HANDLER(r11)
	std     r2, SP_TOC(r1)
#if DC__ABI_PPC64_ELF_V != 2
	ld      r2, 8(r12)
	ld      r0, 0(r12)
	mtctr   r0
#else
	mtctr   r12
#endif
	bctrl

	/* check result type */
	cmpi    cr0, 0, r3, 0x66 /* 'f */
	beq     .f32
	cmpi    cr0, 0, r3, 0x64 /* 'd */
	beq     .f64
.i64:
	ld      r3, SP_RESULT(r1)
	b       .end
.end:

	ld     r2,  SP_TOC(r1)
	ld     r1,  SP_PREV(r1)  /* restore stack pointer */
	ld     r31, -8(r1)       /* restore preserved registers */
	ld     r0,  SP_LR(r1)    /* load link register with return address */
	mtlr   r0
	blr                      /* branch back to link register */
.f32:
	lfs       f1, SP_RESULT(r1)
	b       .end
.f64:
	lfd       f1, SP_RESULT(r1)
	b       .end
