/*
 * Mesa 3-D graphics library
 *
 * Copyright (C) 1999-2004  Brian Paul   All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * Check extended CPU capabilities.  Now justs returns the raw CPUID
 * feature information, allowing the higher level code to interpret the
 * results.
 *
 * Written by Holger Waechtler <holger@akaflieg.extern.tu-berlin.de>
 *
 * Cleaned up and simplified by Gareth Hughes <gareth@valinux.com>
 *
 */

/*
 * NOTE: Avoid using spaces in between '(' ')' and arguments, especially
 * with macros like CONST, LLBL that expand to CONCAT(...).  Putting spaces
 * in there will break the build on some platforms.
 */

#include "matypes.h"
#include "assyntax.h"
#include "common_x86_features.h"

	SEG_TEXT

ALIGNTEXT4
GLOBL GLNAME(_mesa_x86_has_cpuid)
HIDDEN(_mesa_x86_has_cpuid)
GLNAME(_mesa_x86_has_cpuid):

	/* Test for the CPUID command.  If the ID Flag bit in EFLAGS
	 * (bit 21) is writable, the CPUID command is present */
	PUSHF_L
	POP_L	(EAX)
	MOV_L	(EAX, ECX)
	XOR_L	(CONST(0x00200000), EAX)
	PUSH_L	(EAX)
	POPF_L
	PUSHF_L
	POP_L	(EAX)

	/* Verify the ID Flag bit has been written. */
	CMP_L	(ECX, EAX)
	SETNE	(AL)
	XOR_L	(CONST(0xff), EAX)

	RET


ALIGNTEXT4
GLOBL GLNAME(_mesa_x86_cpuid)
HIDDEN(_mesa_x86_cpuid)
GLNAME(_mesa_x86_cpuid):

	MOV_L	(REGOFF(4, ESP), EAX)		/* cpuid op */
	PUSH_L	(EDI)
	PUSH_L	(EBX)

	CPUID

	MOV_L	(REGOFF(16, ESP), EDI)	/* *eax */
	MOV_L	(EAX, REGIND(EDI))
	MOV_L	(REGOFF(20, ESP), EDI)	/* *ebx */
	MOV_L	(EBX, REGIND(EDI))
	MOV_L	(REGOFF(24, ESP), EDI)	/* *ecx */
	MOV_L	(ECX, REGIND(EDI))
	MOV_L	(REGOFF(28, ESP), EDI)	/* *edx */
	MOV_L	(EDX, REGIND(EDI))

	POP_L	(EBX)
	POP_L	(EDI)
	RET

ALIGNTEXT4
GLOBL GLNAME(_mesa_x86_cpuid_eax)
HIDDEN(_mesa_x86_cpuid_eax)
GLNAME(_mesa_x86_cpuid_eax):

	MOV_L	(REGOFF(4, ESP), EAX)		/* cpuid op */
	PUSH_L	(EBX)

	CPUID

	POP_L	(EBX)
	RET

ALIGNTEXT4
GLOBL GLNAME(_mesa_x86_cpuid_ebx)
HIDDEN(_mesa_x86_cpuid_ebx)
GLNAME(_mesa_x86_cpuid_ebx):

	MOV_L	(REGOFF(4, ESP), EAX)		/* cpuid op */
	PUSH_L	(EBX)

	CPUID
	MOV_L	(EBX, EAX)			/* return EBX */

	POP_L	(EBX)
	RET

ALIGNTEXT4
GLOBL GLNAME(_mesa_x86_cpuid_ecx)
HIDDEN(_mesa_x86_cpuid_ecx)
GLNAME(_mesa_x86_cpuid_ecx):

	MOV_L	(REGOFF(4, ESP), EAX)		/* cpuid op */
	PUSH_L	(EBX)

	CPUID
	MOV_L	(ECX, EAX)			/* return ECX */

	POP_L	(EBX)
	RET

ALIGNTEXT4
GLOBL GLNAME(_mesa_x86_cpuid_edx)
HIDDEN(_mesa_x86_cpuid_edx)
GLNAME(_mesa_x86_cpuid_edx):

	MOV_L	(REGOFF(4, ESP), EAX)		/* cpuid op */
	PUSH_L	(EBX)

	CPUID
	MOV_L	(EDX, EAX)			/* return EDX */

	POP_L	(EBX)
	RET

#ifdef USE_SSE_ASM
/* Execute an SSE instruction to see if the operating system correctly
 * supports SSE.  A signal handler for SIGILL should have been set
 * before calling this function, otherwise this could kill the client
 * application.
 *
 *        -----> !!!! ATTENTION DEVELOPERS !!!! <-----
 *
 * If you're debugging with gdb and you get stopped in this function,
 * just type 'continue'!  Execution will proceed normally.
 * See freedesktop.org bug #1709 for more info.
 */
ALIGNTEXT4
GLOBL GLNAME( _mesa_test_os_sse_support )
HIDDEN(_mesa_test_os_sse_support)
GLNAME( _mesa_test_os_sse_support ):

	XORPS	( XMM0, XMM0 )

	RET


/* Perform an SSE divide-by-zero to see if the operating system
 * correctly supports unmasked SIMD FPU exceptions.  Signal handlers for
 * SIGILL and SIGFPE should have been set before calling this function,
 * otherwise this could kill the client application.
 */
ALIGNTEXT4
GLOBL GLNAME( _mesa_test_os_sse_exception_support )
HIDDEN(_mesa_test_os_sse_exception_support)
GLNAME( _mesa_test_os_sse_exception_support ):

	PUSH_L	( EBP )
	MOV_L	( ESP, EBP )
	SUB_L	( CONST( 8 ), ESP )

	/* Save the original MXCSR register value.
	 */
	STMXCSR	( REGOFF( -4, EBP ) )

	/* Unmask the divide-by-zero exception and perform one.
	 */
	STMXCSR	( REGOFF( -8, EBP ) )
	AND_L	( CONST( 0xfffffdff ), REGOFF( -8, EBP ) )
	LDMXCSR	( REGOFF( -8, EBP ) )

	XORPS	( XMM0, XMM0 )

	PUSH_L	( CONST( 0x3f800000 ) )
	PUSH_L	( CONST( 0x3f800000 ) )
	PUSH_L	( CONST( 0x3f800000 ) )
	PUSH_L	( CONST( 0x3f800000 ) )

	MOVUPS	( REGIND( ESP ), XMM1 )

	DIVPS	( XMM0, XMM1 )

	/* Restore the original MXCSR register value.
	 */
	LDMXCSR	( REGOFF( -4, EBP ) )

	LEAVE
	RET

#endif

	
#if defined (__ELF__) && defined (__linux__)
	.section .note.GNU-stack,"",%progbits
#endif
