/*
 * Copyright (c) 2021 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
/* Copyright (c) 1991 NeXT Computer, Inc.  All rights reserved.
 *
 *	File:	architecture/i386/asm_help.h
 *	Author:	Mike DeMoney, NeXT Computer, Inc.
 *	Modified for i386 by: Bruce Martin, NeXT Computer, Inc.
 *
 *	This header file defines macros useful when writing assembly code
 *	for the Intel i386 family processors.
 *
 * HISTORY
 * 10-Mar-92  Bruce Martin (bmartin@next.com)
 *	Adapted to i386
 * 23-Jan-91  Mike DeMoney (mike@next.com)
 *	Created.
 */

#ifndef	_ARCH_I386_ASM_HELP_H_
#define	_ARCH_I386_ASM_HELP_H_

#include	<architecture/i386/reg_help.h>

#ifdef	__ASSEMBLER__

/* Unwind Prologue added to each function to indicate the start of the unwind information. */
#define UNWIND_PROLOGUE \
.cfi_sections .eh_frame ;\
.cfi_startproc          ;

/* Unwind Epilogue added to each function to indicate the end of the unwind information */
#define UNWIND_EPILOGUE .cfi_endproc

#define ALIGN						\
	.align	2, 0x90

#define	ROUND_TO_STACK(len)				\
	(((len) + STACK_INCR - 1) / STACK_INCR * STACK_INCR)

#ifdef notdef
#if defined(__i386__)
#define CALL_MCOUNT						\
	pushl	%ebp						;\
	movl	%esp, %ebp					;\
	.data							;\
	1: .long 0						;\
	.text							;\
	lea 9b,%edx						;\
	call mcount						;\
	popl	%ebp						;
#elif defined(__x86_64__)
#define CALL_MCOUNT						\
	pushq	%rbp						;\
	movq	%rsp, %rbp					;\
	.data							;\
	1: .quad 0						;\
	.text							;\
	lea 9b,%r13						;\
	call mcount						;\
	popq	%rbp					;
#endif
#else
#define CALL_MCOUNT
#endif

/*
 * Prologue for functions that may call other functions.  Saves
 * registers and sets up a C frame.
 */
#if defined(__i386__)
#define NESTED_FUNCTION_PROLOGUE(localvarsize)			\
	.set	L__framesize,ROUND_TO_STACK(localvarsize)	;\
	.set	L__nested_function, 1				;\
	CALL_MCOUNT						\
	.if L__framesize						;\
	  pushl	%ebp						;\
	  movl	%esp, %ebp					;\
	  subl	$L__framesize, %esp				;\
	.endif							;\
	pushl	%edi						;\
	pushl	%esi						;\
	pushl	%ebx
#elif defined(__x86_64__)
#define NESTED_FUNCTION_PROLOGUE(localvarsize)			\
	UNWIND_PROLOGUE										;\
	.set	L__framesize,ROUND_TO_STACK(localvarsize)	;\
	.set	L__nested_function, 1				;\
	CALL_MCOUNT						\
	.if L__framesize						;\
	  pushq	%rbp						;\
	  movq	%rsp, %rbp					;\
	  subq	$L__framesize, %rsp				;\
	.endif							;
#endif

/*
 * Prologue for functions that do not call other functions.  Does not
 * save registers (this is the functions responsibility).  Does set
 * up a C frame.
 */
#if defined(__i386__)
#define LEAF_FUNCTION_PROLOGUE(localvarsize)			\
	.set	L__framesize,ROUND_TO_STACK(localvarsize)	;\
	.set	L__nested_function, 0				;\
	CALL_MCOUNT						\
	.if L__framesize						;\
	  pushl	%ebp						;\
	  movl	%esp, %ebp					;\
	  subl	$L__framesize, %esp				;\
	.endif
#elif defined(__x86_64__)
#define LEAF_FUNCTION_PROLOGUE(localvarsize)			\
	UNWIND_PROLOGUE						;\
	.set	L__framesize,ROUND_TO_STACK(localvarsize)	;\
	.set	L__nested_function, 0				;\
	CALL_MCOUNT						\
	.if L__framesize						;\
	  pushq	%rbp						;\
	  movq	%rsp, %rbp					;\
	  subq	$L__framesize, %rsp				;\
	.endif
#endif

/*
 * Epilogue for any function.
 *
 * We assume that all Leaf functions will be responsible for saving any
 * local registers they clobber.
 */
#if defined(__i386__)
#define FUNCTION_EPILOGUE					\
	.if L__nested_function					;\
	  popl	%ebx						;\
	  popl	%esi						;\
	  popl	%edi						;\
	.endif							;\
	.if L__framesize						;\
	  movl	%ebp, %esp					;\
	  popl	%ebp						;\
	.endif							;\
	ret
#elif defined(__x86_64__)
#define FUNCTION_EPILOGUE					\
	.if L__framesize						;\
	  movq	%rbp, %rsp					;\
	  popq	%rbp						;\
	.endif							;\
	ret								;\
	UNWIND_EPILOGUE
#endif

/*
 * Macros for declaring procedures
 *
 * Use of these macros allows ctags to have a predictable way
 * to find various types of declarations.  They also simplify
 * inserting appropriate symbol table information.
 *
 * NOTE: these simple stubs will be replaced with more
 * complicated versions once we know what the linker and gdb
 * will require as far as register use masks and frame declarations.
 * These macros may also be ifdef'ed in the future to contain profiling
 * code.
 *
 */

/*
 * TEXT -- declare start of text segment
 */
#define	TEXT						\
	.text

/*
 * DATA -- declare start of data segment
 */
#define DATA						\
	.data

/*
 * LEAF -- declare global leaf procedure
 * NOTE: Control SHOULD NOT FLOW into a LEAF!  A LEAF should only
 * be jumped to.  (A leaf may do an align.)  Use a LABEL() if you
 * need control to flow into the label.
 */
#define	LEAF(name, localvarsize)			\
	.globl	name					;\
	ALIGN						;\
name:							;\
	LEAF_FUNCTION_PROLOGUE(localvarsize)

/*
 * X_LEAF -- declare alternate global label for leaf
 */
#define	X_LEAF(name, value)				\
	.globl	name					;\
	.set	name,value

/*
 * P_LEAF -- declare private leaf procedure
 */
#define	P_LEAF(name, localvarsize)			\
	ALIGN						;\
name:							;\
	LEAF_FUNCTION_PROLOGUE(localvarsize)

/*
 * LABEL -- declare a global code label
 * MUST be used (rather than LEAF, NESTED, etc) if control
 * "flows into" the label.
 */
#define	LABEL(name)					\
	.globl	name					;\
name:

/*
 * NESTED -- declare procedure that invokes other procedures
 */
#define	NESTED(name, localvarsize)			\
	.globl	name					;\
	ALIGN						;\
name:							;\
	NESTED_FUNCTION_PROLOGUE(localvarsize)

/*
 * X_NESTED -- declare alternate global label for nested proc
 */
#define	X_NESTED(name, value)				\
	.globl	name					;\
	.set	name,value

/*
 * P_NESTED -- declare private nested procedure
 */
#define	P_NESTED(name, localvarsize)			\
	ALIGN						;\
name:							;\
	NESTED_FUNCTION_PROLOGUE(localvarsize)

/*
 * END -- mark end of procedure
 */
#define	END(name)					\
	FUNCTION_EPILOGUE


/*
 * Storage definition macros
 * The main purpose of these is to allow an easy handle for ctags
 */

/*
 * IMPORT -- import symbol
 */
#define	IMPORT(name)					\
	.reference	name

/*
 * ABS -- declare global absolute symbol
 */
#define	ABS(name, value)				\
	.globl	name					;\
	.set	name,value

/*
 * P_ABS -- declare private absolute symbol
 */
#define	P_ABS(name, value)				\
	.set	name,value

/*
 * EXPORT -- declare global label for data
 */
#define	EXPORT(name)					\
	.globl	name					;\
name:

/*
 * BSS -- declare global zero'ed storage
 */
#define	BSS(name,size)					\
	.comm	name,size


/*
 * P_BSS -- declare private zero'ed storage
 */
#define	P_BSS(name,size)				\
	.lcomm	name,size

/*
 * dynamic/PIC macros for routines which reference external symbols
 */

#if defined(__DYNAMIC__)
#if defined(__i386__)
#define PICIFY(var)					\
	call	1f					; \
1:							; \
	popl	%edx					; \
	movl	L ## var ## __non_lazy_ptr-1b(%edx),%edx
#elif defined(__x86_64__)
#define PICIFY(var)					\
	movq	var@GOTPCREL(%rip),%r11
#endif

#if defined(__i386__)
#define CALL_EXTERN_AGAIN(func)	\
	PICIFY(func)		; \
	call	*%edx
#elif defined(__x86_64__)
#define CALL_EXTERN_AGAIN(func)	\
	call	func
#endif

#if defined(__i386__)
#define NON_LAZY_STUB(var)	\
.section __IMPORT,__pointers,non_lazy_symbol_pointers	; \
L ## var ## __non_lazy_ptr:	; \
.indirect_symbol var		; \
.long 0				; \
.text
#elif defined(__x86_64__)
#define NON_LAZY_STUB(var)
#endif

#define CALL_EXTERN(func)	\
	CALL_EXTERN_AGAIN(func)	; \
	NON_LAZY_STUB(func)

#if defined(__i386__)
#define BRANCH_EXTERN(func)	\
	PICIFY(func)		; \
	jmp	*%edx		; \
	NON_LAZY_STUB(func)
#elif defined(__x86_64__)
#define BRANCH_EXTERN(func)	\
	jmp	func
#endif

#if defined(__i386__)
#define PUSH_EXTERN(var)	\
	PICIFY(var)		; \
	movl	(%edx),%edx	; \
	pushl	%edx		; \
	NON_LAZY_STUB(var)
#endif

#if defined(__i386__)
#define REG_TO_EXTERN(reg, var)	\
	PICIFY(var)		; \
	movl	reg, (%edx)	; \
	NON_LAZY_STUB(var)
#elif defined(__x86_64__)
#define REG_TO_EXTERN(reg, var)	\
	PICIFY(var)		; \
	mov		reg, (%r11)
#endif

#if defined(__i386__)
#define EXTERN_TO_REG(var, reg)				\
	call	1f					; \
1:							; \
	popl	%edx					; \
	movl	L ## var ##__non_lazy_ptr-1b(%edx),reg	; \
	NON_LAZY_STUB(var)
#elif defined(__x86_64__)
#define EXTERN_TO_REG(var, reg)				\
	PICIFY(var)		; \
	mov    (%r11), reg
#endif

#else
#define BRANCH_EXTERN(func)	jmp	func
#define PUSH_EXTERN(var)	push	var
#define CALL_EXTERN(func)	call	func
#define CALL_EXTERN_AGAIN(func)	call	func
#if defined(__i386__)
#define REG_TO_EXTERN(reg, var)	mov	reg, var
#define EXTERN_TO_REG(var, reg)	mov	$ ## var, reg
#elif defined(__x86_64__)
#define REG_TO_EXTERN(reg, var)	mov	reg, var ## (%rip)
#define EXTERN_TO_REG(var, reg)	mov	var ## (%rip), reg
#endif
#endif

#endif	/* __ASSEMBLER__ */

#endif	/* _ARCH_I386_ASM_HELP_H_ */
