/***************************************************************************
 *                                                                         *
 *   LinuxSampler - modular, streaming capable sampler                     *
 *                                                                         *
 *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
 *   Copyright (C) 2005 - 2025 Christian Schoenebeck                       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
 *   MA  02111-1307  USA                                                   *
 ***************************************************************************/

#include "Features.h"

#if CONFIG_ASM && ARCH_X86
bool Features::bMMX(false);
bool Features::bSSE(false);
bool Features::bSSE2(false);

void Features::detect() {
#ifdef __x86_64__
    int64_t edx;
    __asm__ __volatile__ (
        "mov %%rbx,%%rdi\n\t" /*save PIC register*/
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "mov %%rdi,%%rbx\n\t" /*restore PIC register*/
        : "=d" (edx)
        : : "%rax", "%rcx", "%rdi"
    );
#else
    int edx;
    __asm__ __volatile__ (
        "movl %%ebx,%%edi\n\t" /*save PIC register*/
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "movl %%edi,%%ebx\n\t" /*restore PIC register*/
        : "=d" (edx)
        : : "%eax", "%ecx", "%edi"
    );
#endif
    bMMX = (edx & 0x00800000);
    bSSE = (edx & 0x02000000);
    bSSE2 = (edx & 0x04000000);
}
#else
void Features::detect() {}
#endif // CONFIG_ASM && ARCH_X86

bool Features::enableDenormalsAreZeroMode() {
    #if CONFIG_ASM && ARCH_X86
    if (supportsSSE2()) {
        unsigned int x;
        __asm__ __volatile__ (
            "stmxcsr %0\n\t"
            "orl     $0x8040, %0\n\t" // set DAZ bit (6) and FTZ bit (15)
            "ldmxcsr %0\n\t"
            : "+m" (x)
        );
        return true;
    }
    return false;
    #elif CONFIG_ASM && (defined(__arm__) || defined(__ARM_ARCH))
    # if defined(__aarch64__) // 64-bit ARM (ARMv8 or higher)
    uint64_t x;
    __asm__ __volatile__ (
        "mrs %0, fpcr\n\t"
        "orr %0, %0, #0x01000000\n\t" // set FZ bit (24)
        "msr fpcr, %0\n\t"
        : "=r" (x)
        : "0" (x)
    );
    # else // 32-bit ARM (ARMv7* or older)
    uint32_t x;
    __asm__ __volatile__ (
        "vmrs %0, fpscr\n\t"
        "orr %0, %0, #0x01000000\n\t" // set FZ bit (24)
        "vmsr fpscr, %0\n\t"
        : "=r" (x)
        : "0" (x)
    );
    # endif
    return true;
    #else
    return false;
    #endif // CONFIG_ASM && ARCH_X86
}

String Features::featuresAsString() {
    String sFeatures = "none";
    #if CONFIG_ASM && ARCH_X86    
    if (supportsMMX())  sFeatures  =  "MMX";
    if (supportsSSE())  sFeatures += " SSE";
    if (supportsSSE2()) sFeatures += " SSE2";
    #else
    sFeatures = "disabled at compile time";
    #endif // CONFIG_ASM && ARCH_X86
    return sFeatures;
}
