//
//  AngelCode Scripting Library
//  Copyright (c) 2024 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
//


// Assembly routines for the 64bit RISC-V call convention used for Linux

// Compile with GCC/GAS

#if !defined(AS_MAX_PORTABILITY)

#if defined(__riscv) && defined(__LP64__)

	.file "as_callfunc_riscv64_gcc.S"
	.option pic
	.attribute unaligned_access, 0
	.attribute stack_align, 16
	.text
	.align 1
	.globl CallRiscVFunc
	.type CallRiscVFunc, @function
CallRiscVFunc:
	.cfi_startproc

	// Setup call stack
	addi    sp,sp,-336        // reserve bytes on stack (aligned to 16bytes). 256 bytes (32*8) is reserved for values that will be pushed on the stack for the function call, the rest is for local backup of registers
	.cfi_def_cfa_offset 336
	sd      ra,328(sp)        // backup return address on stack, just below original stack frame pointer
	sd      s0,320(sp)        // backup frame pointer on stack, below return address
	.cfi_offset 1, -8
	.cfi_offset 8, -16
	addi    s0,sp,336         // load new frame pointer with the reserved space
	.cfi_def_cfa 8,0

	// Backup arguments on stack
	// TODO: skip the backup and move them into the temp registers directly
	sd      a0,-24(s0)        // store func arg on stack, below backup of s0
	sd      a1,-32(s0)        // store retfloat arg on stack
	sd      a2,-40(s0)        // store argValues arg on stack
	sd      a3,-48(s0)        // store numRegularValues arg on stack
	sd      a4,-56(s0)        // store numFloatValues arg on stack
	sd      a5,-64(s0)        // store numStackValues arg on stack

	ld      t1,-40(s0)        // load argValues arg into t1

	// Load regular values into regular registers
	// TODO: skip ahead to the number of generic args
	ld      a7,56(t1)         // a7 = argValues[7]
	ld      a6,48(t1)         // a6 = argValues[6]
	ld      a5,40(t1)         // a5 = argValues[5]
	ld      a4,32(t1)         // a4 = argValues[4]
	ld      a3,24(t1)         // a3 = argValues[3]
	ld      a2,16(t1)         // a2 = argValues[2]
	ld      a1,8(t1)          // a1 = argValues[1]
	ld      a0,0(t1)          // a0 = argValues[0]
	
	// Load float values into float registers
	// TODO: skip ahead to the number of float args
	fld     fa7,120(t1)       // fa7 = argValues[15]
	fld     fa6,112(t1)       // fa6 = argValues[14]
	fld     fa5,104(t1)       // fa5 = argValues[13]
	fld     fa4,96(t1)        // fa4 = argValues[12]
	fld     fa3,88(t1)        // fa3 = argValues[11]
	fld     fa2,80(t1)        // fa2 = argValues[10]
	fld     fa1,72(t1)        // fa1 = argValues[9]
	fld     fa0,64(t1)        // fa0 = argValues[8]

	// Push the remaining args on the stack
	addi    t1,t1,128         // t1 = &argValues[16]
	mv      t3,sp             // t3 = sp
	ld      t2,-64(s0)        // t2 = numStackValues
	beq     t2,x0,.L_nomore   // jump if t2 == 0
.L_next:
	addi    t2,t2,-1          // t2--
    ld      t5,0(t1)          // t5 value from argValues
	sd      t5,0(t3)          // store the value on the stack
	addi    t3,t3,8           // move t3 to the next slot on the stack
	addi    t1,t1,8           // move to the next value in argValues
	bne     t2,x0,.L_next     // reiterate if t2 is not zero
.L_nomore:

	// Call the function
	ld      t1,-24(s0)        // load func arg to t1
	jalr    t1                // call the function in func arg
	nop

	// If the function returns a float value, then retrieve that
	ld      a5,-32(s0)        // restore retfloat
	sext.w  a4,a5             // load lower 32bit word of retfloat into a4
	li      a5,1              // set a5 = 1
	bne     a4,a5,.L_nofloat  // jump if a4 != 1  TODO: rewrite to compare with x0 which is hardwired to 0 so there is no need to set a5
	fsd     fa0,-32(s0)       // store the raw returned 64bit float/double value on the stack (where retfloat was)
	ld      a0,-32(s0)        // load the raw 64bit value into a0 for return
	fsd     fa1,-32(s0)       // store the raw returned 64bit float/double value on the stack (where retfloat was)
	ld      a1,-32(s0)        // load the raw 64bit value into a1 for return
.L_nofloat:

	// Clean up call stack
	ld      ra,328(sp)        // restore return address from stack
	.cfi_restore 1
	ld      s0,320(sp)        // restore frame pointer from stack
	.cfi_restore 8
	.cfi_def_cfa 2, 336
	addi    sp,sp,336         // clear reserved space from stack
	.cfi_def_cfa_offset 0
	jr      ra

	.cfi_endproc
	.size CallRiscVFunc, .-CallRiscVFunc

#endif /* __riscv && __LP64__ */

#endif /* !AS_MAX_PORTABILITY */
