Basic Notation
The basic scheme of use of asm
or __asm__
or _asm
or __asm
in C C++ is as follows (using GCC as reference):
asm [volatile] ( "SEU CODIGO\n\t"
"EM\n\t"
"ASSEMBLY"
: OperadoresDeSaída
[ : OperadoresDeEntrada
[ : Clobbers ] ])
This notation changes depending on the compiler.
Examples
I find it easier to explain using some example. Look at the ideone.
Assuming you have the following variables:
// Criando variáveis para interagir com assembly:
int foo, bar, var;
You can interact with them using inline assembly
as follows:
// Em C, seria:
// foo = 1;
// bar = 2;
// var = 3;
asm volatile ("movl $1, %0;" // código assembly
"movl $2, %1;"
"movl $3, %2;"
: "=r" (foo), "=r" (bar), "=r" (var) // variáveis de saída
);
The =r
indicate to the compiler that the result of that instruction must be sent by means of a register for the variable %N
, where N is the index. You can also use =g
letting the compiler decide which way to send the value. More details in the documentation.
// Em C, seria:
// bar = foo * 2;
asm volatile ("movl $2, %%eax;" // eax = 2
"imul %%ebx, %%eax;" // eax * ebx
"movl %%eax, %0;" // faz bar igual ao resultado.
: "=r" (bar) // variáveis de saída
: "b" (foo) // variáveis de entrada (ebx = foo)
);
In this case, the compiler passes the value of foo to the EBX register, and then uses it in the informed Assembly code.
// Em C, seria:
// var = bar;
asm volatile ("movl %0, %%eax;"
"movl %%eax, %1;"
: "=r" (var) // saída
: "b" (bar) // entrada
: "%eax" // clobbers
);
Makes var
equal to bar
using the EAX register (note the use of indexes in %0 and %1). The third parameter (Clobbers) serves to tell the compiler that the EAX register will be used. This way, before executing your Assembly code, the compiler will save any content present in EAX that will be used after your code, releasing the EAX for you.
To get the CPU manufacturer
Then, you can use the following code to call get the CPU manufacturer. Using cpuid
:
asm volatile ("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
: "a" (op));
Where, eax
, ebx
, ecx
and edx
are the values of the registers and op
is the cpuid function that will be called. With this, variables eax
and etc will receive the cpuid return, which you will use to print the processor manufacturer.
In Windows, you can also call up the above code using the following function:
int regs[4]; // recebe eax, ebx, ecx, edx
int op = 0; // código da função
__cpuid(regs, op);
For this, you must include intrin.h
Example in ideone:
#include <stdio.h>
#include <stdint.h>
#include <cpuid.h>
#include <string.h>
int main(int argc, char **argv)
{
// a função opcode CPUID:
int op;
// registradores:
int eax;
int ebx;
int ecx;
int edx;
// parâmetro zero para CPUID indica que você quer o fabricante.
op = 0;
__asm__ ("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
: "a" (op));
// Receberá os valores de EBX, ECX e EDX para sistemas 32bits:
char vendor[sizeof(int) * 3 + 1];
strncpy(vendor, (const char*) &ebx, sizeof(int));
strncpy(&vendor[8], (const char*) &ecx, sizeof(int));
strncpy(&vendor[4], (const char*) &edx, sizeof(int));
vendor[12] = '\0'; // terminador nulo
printf("CPU: %s", vendor);
return 0;
}
The return will depend on the CPU and will show only the manufacturer, using the following notation:
"AMDisbetter!" ou "AuthenticAMD" -> "AMD";
"GenuineIntel" -> "Intel"
"VIA VIA VIA " -> "VIA"
"CentaurHauls" -> "Centaur"
"CyrixInstead" -> "Cyrix"
"TransmetaCPU" ou "GenuineTMx86" -> "Transmeta"
"Geode by NSC" -> "National Semiconductor"
"NexGenDriven" -> "NexGen"
"RiseRiseRise" -> "Rise"
"SiS SiS SiS " -> "SiS"
"UMC UMC UMC " -> "UMC"
"Vortex86 SoC" -> "Vortex"
"KVMKVMKVMKVM" -> "KVM"
"Microsoft Hv" -> "Microsoft Hyper-V"
"VMwareVMware" -> "VMware"
"XenVMMXenVMM" -> "Xen HVM"
Note: this code is for x86. To know which CPU model is a larger job.
Reference: Playing with cpuid
Related: How to check which technologies the CPU supports at runtime?
– Lucas Lima