// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors

//go:build darwin || freebsd || linux || netbsd

#include "textflag.h"
#include "go_asm.h"
#include "funcdata.h"
#include "abi_arm64.h"

TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
	NO_LOCAL_POINTERS

	// On entry, the trampoline in zcallback_darwin_arm64.s left
	// the callback index in R12 (which is volatile in the C ABI).

	// Save callback register arguments R0-R7 and F0-F7.
	// We do this at the top of the frame so they're contiguous with stack arguments.
	SUB   $(16*8), RSP, R14
	FSTPD (F0, F1), (0*8)(R14)
	FSTPD (F2, F3), (2*8)(R14)
	FSTPD (F4, F5), (4*8)(R14)
	FSTPD (F6, F7), (6*8)(R14)
	STP   (R0, R1), (8*8)(R14)
	STP   (R2, R3), (10*8)(R14)
	STP   (R4, R5), (12*8)(R14)
	STP   (R6, R7), (14*8)(R14)

	// Adjust SP by frame size.
	SUB $(26*8), RSP

	// It is important to save R27 because the go assembler
	// uses it for move instructions for a variable.
	// This line:
	// MOVD ·callbackWrap_call(SB), R0
	// Creates the instructions:
	// ADRP 14335(PC), R27
	// MOVD 388(27), R0
	// R27 is a callee saved register so we are responsible
	// for ensuring its value doesn't change. So save it and
	// restore it at the end of this function.
	// R30 is the link register. crosscall2 doesn't save it
	// so it's saved here.
	STP (R27, R30), 0(RSP)

	// Create a struct callbackArgs on our stack.
	MOVD $(callbackArgs__size)(RSP), R13
	MOVD R12, callbackArgs_index(R13)    // callback index
	MOVD R14, callbackArgs_args(R13)     // address of args vector
	MOVD ZR, callbackArgs_result(R13)    // result

	// Move parameters into registers
	// Get the ABIInternal function pointer
	// without <ABIInternal> by using a closure.
	MOVD ·callbackWrap_call(SB), R0
	MOVD (R0), R0                   // fn unsafe.Pointer
	MOVD R13, R1                    // frame (&callbackArgs{...})
	MOVD $0, R3                     // ctxt uintptr

	BL crosscall2(SB)

	// Get callback result.
	MOVD $(callbackArgs__size)(RSP), R13
	MOVD callbackArgs_result(R13), R0

	// Restore LR and R27
	LDP 0(RSP), (R27, R30)
	ADD $(26*8), RSP

	RET
