--------------------------------
-- Ivan Levashew aka OCTAGRAM --
--              Barnaul, 2009 --
--------------------------------

-- Turbo_Private.CPUID, x86 version

with Interfaces, System.Machine_Code, Ada.Characters.Latin_1;

use  Interfaces, System.Machine_Code, Ada.Characters.Latin_1;

package body Turbo_Private.CPUID is

   CPU_Arch_Var : Integer := 0; -- 0 means "not detected yet"
   pragma Atomic (CPU_Arch_Var);

   ----------------
   -- Detect_CPU --
   ----------------

   procedure Detect_CPU is
      Dummy, Result : Integer_32;
      -- Q: What is this Dummy doing here?
      -- A: The manual named...
      -- Using the GNU Compiler Collection (GCC)
      -- 5.37 Assembler Instructions with C Expression Operands
      -- ...states the following:
      -- There is no way for you to specify that an input operand is
      -- modified without also specifying it as an output operand.
   begin
      -- 386 is guaranteed
      -- 486 has X86_EFLAGS_AC = 16#0004_0000#
      -- 586 has X86_EFLAGS_ID = 16#0020_0000#
      Asm (
         "pushfl"         & LF & HT &
            "pushfl"      & LF & HT &
            "popl %0"     & LF & HT &
            "movl %0, %1" & LF & HT &
            "xorl %2, %0" & LF & HT &
            "pushl %0"    & LF & HT &
            "popfl"       & LF & HT &
            "pushfl"      & LF & HT &
            "popl %0"     & LF & HT &
            "xorl %0, %1" & LF & HT &
         "popfl",
          Outputs  => (Integer_32'Asm_Output ("=&r", Dummy),
                       Integer_32'Asm_Output ("=&r", Result)),
          Inputs   => (Integer_32'Asm_Input ("i", 16#0024_0000#))
      );
      case Result is
         when 16#0000_0000# => CPU_Arch_Var := 3;
         when 16#0004_0000# => CPU_Arch_Var := 4;
         when 16#0024_0000# => CPU_Arch_Var := 5;
            -- Detect further CPU here
         when others => raise Program_Error with "Failed to detect CPU family";
      end case;
   end Detect_CPU;
   
   --------------
   -- CPU_Arch --
   --------------

   function CPU_Arch return Integer is
   begin
      if CPU_Arch_Var = 0 then
         Detect_CPU;
      end if;
      return CPU_Arch_Var;
   end CPU_Arch;

end Turbo_Private.CPUID;
