+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// CPU dispatch support for x86
+///
+/// (c) 2019 Straylight/Edgeware
+///
+
+///----- Licensing notice ---------------------------------------------------
+///
+/// This file is part of Catacomb.
+///
+/// Catacomb is free software: you can redistribute it and/or modify it
+/// under the terms of the GNU Library General Public License as published
+/// by the Free Software Foundation; either version 2 of the License, or
+/// (at your option) any later version.
+///
+/// Catacomb 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
+/// Library General Public License for more details.
+///
+/// You should have received a copy of the GNU Library General Public
+/// License along with Catacomb. If not, write to the Free Software
+/// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+/// USA.
+
+///--------------------------------------------------------------------------
+/// Preliminaries.
+
+#include "config.h"
+#include "asm-common.h"
+
+ EFLAGS_ID = 1 << 21
+
+ .text
+
+///--------------------------------------------------------------------------
+/// Probing for CPUID.
+
+FUNC(dispatch_x86ish_cpuid)
+ // Enter with a pointer to 16 bytes of storage for the output A, B,
+ // C, D values in the first argument, and input A and C values in the
+ // second and third. Fill the output buffer with `cpuid' results and
+ // return zero if we can; otherwise fill with zero and return -1.
+
+#if CPUFAM_X86
+ pushreg ebx
+ pushreg edi
+ mov edi, [esp + 12]
+ mov eax, [esp + 16]
+ mov ecx, [esp + 20]
+# define OUT edi
+#endif
+#if CPUFAM_AMD64 && ABI_SYSV
+ pushreg rbx
+ mov eax, esi
+ mov ecx, edx
+# define OUT rdi
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+ pushreg rbx
+ mov r9, rcx
+ mov eax, edx
+ mov ecx, r8d
+# define OUT r9
+#endif
+ endprologue
+
+ // First, check that this is even a thing, using the complicated
+ // dance with the flags register.
+ pushf
+ pop R_d(r) // current flags in d
+
+ or R_d(r), EFLAGS_ID // force the id bit on and check it
+ push R_d(r)
+ popf
+ pushf
+ pop R_d(r)
+ test edx, EFLAGS_ID
+ jz 8f
+
+ and R_d(r), ~EFLAGS_ID // force the id bit off and check it
+ push R_d(r)
+ popf
+ pushf
+ pop R_d(r)
+ test edx, EFLAGS_ID
+ jnz 8f
+
+ // OK, that seemed to work.
+ cpuid
+
+ mov [OUT + 0], eax
+ mov [OUT + 4], ebx
+ mov [OUT + 8], ecx
+ mov [OUT + 12], edx
+ xor eax, eax
+
+ // We're done.
+9:
+#if CPUFAM_X86
+ popreg edi
+ popreg ebx
+#endif
+#if CPUFAM_AMD64
+ popreg rbx
+#endif
+ ret
+
+ // Failed.
+8: xor eax, eax
+ mov [OUT + 0], eax
+ mov [OUT + 4], eax
+ mov [OUT + 8], eax
+ mov [OUT + 12], eax
+ mov eax, -1
+ jmp 9b
+ENDFUNC
+
+///--------------------------------------------------------------------------
+/// Probing for XMM register availability.
+
+FUNC(dispatch_x86ish_xmmregisters_p)
+ // Enter with no arguments. Return nonzero if the XMM registers are
+ // usable.
+
+ pushreg R_bp(r)
+ setfp
+ stalloc 512
+ and R_sp(r), ~15
+ endprologue
+
+ // Save the floating point and SIMD registers, and try to clobber
+ // xmm0.
+ fxsave [R_sp(r)]
+ mov eax, [R_sp(r) + 160]
+ xor dword ptr [R_sp(r) + 160], 0xaaaa5555
+ fxrstor [R_sp(r)]
+
+ // Save them again, and read back the low word of xmm0. Undo the
+ // clobbering and restore.
+ fxsave [R_sp(r)]
+ mov ecx, [R_sp(r) + 160]
+ mov [R_sp(r) + 160], eax
+ fxrstor [R_sp(r)]
+
+ // The register are live if we read different things.
+ xor eax, ecx
+
+ // Done.
+ dropfp
+ popreg R_bp(r)
+ ret
+ENDFUNC
+
+///----- That's all, folks --------------------------------------------------