6

The c++ way to do it is here (under Windows).

The same answer but under Linux using GCC.

Excerpt of the relevant asm code as I understand it:

mov     eax, 1
cpuid
mov     features, edx

I'm not very comfortable at BASM.

My Question:

I need to wrap the test as follows

function IsSSE2: Boolean;
begin
  try
    Result := False;
    //
    // Some BASM code here
    //
  except
    Result := False;
  end;
end;

Please help me.

Community
  • 1
  • 1
menjaraz
  • 7,551
  • 4
  • 41
  • 81
  • Be careful. Both the CPU and OS must support SSE2. The OS must support it because the SSE registers get saved to memory on a context switch, and the OS has to supply the area of memory. That's why its sometimes not enough to test for the SSE2 cpu feature bit. That's also why you see tests for XSTORE and FXSAVE support. IIRC, they are enabled if the OS supplies the memory area; otherwise the OS disables it (some hand waiving). Its usually not a problem nowadays unless you support older processors and OSes. Also see *Section 11.6.2, Checking for SSE/SSE2 Support* in the Intel Programmers Manual. – jww Oct 12 '17 at 23:26
  • Also see [Determine processor support for SSE2?](https://stackoverflow.com/q/2403660/608639) and [How to check if a CPU supports the SSE3 instruction set?](https://stackoverflow.com/q/6121792/608639). The second question provides details of OS support. – jww Oct 12 '17 at 23:43

3 Answers3

21

You can do that without assembler as well. Works with Windows XP and newer only though.

function IsProcessorFeaturePresent(ProcessorFeature: DWORD): BOOL; stdcall;
  external kernel32 name 'IsProcessorFeaturePresent';

const
  PF_XMMI64_INSTRUCTIONS_AVAILABLE = 10;

function HasSSE2: boolean;
begin
  result := IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
end;
Giel
  • 2,066
  • 20
  • 22
8

I think that this does it:

function SupportsSSE2: LongBool;
const
  CPUID_INTEL_SSE2 = $04000000;
asm
  push ebx
  mov eax, 1
  cpuid
  mov eax, FALSE
  test edx, CPUID_INTEL_SSE2
  jz @END
  mov eax, TRUE
@END:
  pop ebx
end;
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
7

Here is the code used by the graphics32 library to detect processor features:

{$IFDEF WIN64}
  {$DEFINE TARGET_x64}
{$ENDIF}

type
  TCPUInstructionSet = (ciMMX, ciEMMX, ciSSE, ciSSE2, ci3DNow, ci3DNowExt);

const
  CPUISChecks: Array[TCPUInstructionSet] of Cardinal =
    ($800000,  $400000, $2000000, $4000000, $80000000, $40000000);
    {ciMMX  ,  ciEMMX,  ciSSE   , ciSSE2  , ci3DNow ,  ci3DNowExt}

function CPUID_Available: Boolean;
asm
{$IFDEF TARGET_x64}
        MOV       EDX,False
        PUSHFQ
        POP       RAX
        MOV       ECX,EAX
        XOR       EAX,$00200000
        PUSH      RAX
        POPFQ
        PUSHFQ
        POP       RAX
        XOR       ECX,EAX
        JZ        @1
        MOV       EDX,True
@1:     PUSH      RAX
        POPFQ
        MOV       EAX,EDX
{$ELSE}
        MOV       EDX,False
        PUSHFD
        POP       EAX
        MOV       ECX,EAX
        XOR       EAX,$00200000
        PUSH      EAX
        POPFD
        PUSHFD
        POP       EAX
        XOR       ECX,EAX
        JZ        @1
        MOV       EDX,True
@1:     PUSH      EAX
        POPFD
        MOV       EAX,EDX
{$ENDIF}
end;

function CPU_Signature: Integer;
asm
{$IFDEF TARGET_x64}
        PUSH      RBX
        MOV       EAX,1
        CPUID
        POP       RBX
{$ELSE}
        PUSH      EBX
        MOV       EAX,1
        {$IFDEF FPC}
        CPUID
        {$ELSE}
        DW        $A20F   // CPUID
        {$ENDIF}
        POP       EBX
{$ENDIF}
end;

function CPU_Features: Integer;
asm
{$IFDEF TARGET_x64}
        PUSH      RBX
        MOV       EAX,1
        CPUID
        POP       RBX
        MOV       EAX,EDX
{$ELSE}
        PUSH      EBX
        MOV       EAX,1
        {$IFDEF FPC}
        CPUID
        {$ELSE}
        DW        $A20F   // CPUID
        {$ENDIF}
        POP       EBX
        MOV       EAX,EDX
{$ENDIF}
end;

function CPU_ExtensionsAvailable: Boolean;
asm
{$IFDEF TARGET_x64}
        PUSH      RBX
        MOV       @Result, True
        MOV       EAX, $80000000
        CPUID
        CMP       EAX, $80000000
        JBE       @NOEXTENSION
        JMP       @EXIT
        @NOEXTENSION:
        MOV       @Result, False
        @EXIT:
        POP       RBX
{$ELSE}
        PUSH      EBX
        MOV       @Result, True
        MOV       EAX, $80000000
        {$IFDEF FPC}
        CPUID
        {$ELSE}
        DW        $A20F   // CPUID
        {$ENDIF}
        CMP       EAX, $80000000
        JBE       @NOEXTENSION
        JMP       @EXIT
      @NOEXTENSION:
        MOV       @Result, False
      @EXIT:
        POP       EBX
{$ENDIF}
end;

function CPU_ExtFeatures: Integer;
asm
{$IFDEF TARGET_x64}
        PUSH      RBX
        MOV       EAX, $80000001
        CPUID
        POP       RBX
        MOV       EAX,EDX
{$ELSE}
        PUSH      EBX
        MOV       EAX, $80000001
        {$IFDEF FPC}
        CPUID
        {$ELSE}
        DW        $A20F   // CPUID
        {$ENDIF}
        POP       EBX
        MOV       EAX,EDX
{$ENDIF}
end;

function HasInstructionSet(const InstructionSet: TCPUInstructionSet): Boolean;
// Must be implemented for each target CPU on which specific functions rely
begin
  Result := False;
  if not CPUID_Available then Exit;                   // no CPUID available
  if CPU_Signature shr 8 and $0F < 5 then Exit;       // not a Pentium class

  case InstructionSet of
    ci3DNow, ci3DNowExt:
      {$IFNDEF FPC}
      if not CPU_ExtensionsAvailable or (CPU_ExtFeatures and CPUISChecks[InstructionSet] = 0) then
      {$ENDIF}
        Exit;
    ciEMMX:
      begin
        // check for SSE, necessary for Intel CPUs because they don't implement the
        // extended info
        if (CPU_Features and CPUISChecks[ciSSE] = 0) and
          (not CPU_ExtensionsAvailable or (CPU_ExtFeatures and CPUISChecks[ciEMMX] = 0)) then
          Exit;
      end;
  else
    if CPU_Features and CPUISChecks[InstructionSet] = 0 then
      Exit; // return -> instruction set not supported
    end;

  Result := True;
end;

You can call HasInstructionSet(ciSSE2) to discover what you need.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thank you David! It never crosses my mind that I can readily have it in `Graphic32`. – menjaraz Feb 18 '12 at 12:45
  • It was at the front of my mind because I'm doing a 64 bit port at the moment and had some problems with unaligned memory and SSE2 instructions under 64 bit! – David Heffernan Feb 18 '12 at 12:46
  • This could pose a problem on Cyrix/NextGen chips where ID has to be left set to leave CPUID facility in the "enabled" state. – OnTheFly Feb 18 '12 at 16:56