;name: cpuid.asm
;build: nasm -felf64 cpuid.asm -o cpuid.o
;       ld -s -melf_x86_64 -o cpuid cpuid.o 
;description:  Checks if CPUID instruction is supported and if yes, shows also the Vendor ID

[list -]
    %include "unistd.inc"
    %include "cpu/cpu.inc"
[list +]

section .data

    output:         db  "The processor Vendor ID is '"
    .regEBXvalue:   db  "xxxx"
    .regEDXvalue:   db  "xxxx"
    .regECXvalue:   db  "xxxx"
                    db  "'", 10
    .len:           equ $-output

    nosupport:      db  "CPUID is not supported", 10
    .len:           equ $-nosupport

section .text

global _start

    ;assemble in 32 bit mode
    bits 32
    ;returns 1 if CPUID is supported, 0 otherwise (ZF is also set accordingly)
    pushfd                                  ;get 32 bits flags
    pop     eax                             ;and put in EAX
    mov     ecx,eax                         ;save the original flags state in ECX 
    xor     eax,0x200000                    ;flip bit 21 
    push    eax                             ;and put via EAX
    popfd                                   ;on stack
    pushfd                                  ;get the flags again
    pop     eax                             ;put in EAX
    xor     eax,ecx                         ;and check if bit 21 is the same as stored before
    shr     eax,21                          ;move bit 21 to bit 0
    and     eax,1                           ;and mask others
    push    ecx                             ;
    popfd                                   ;and restore original flags
    jnz     .supported
    bits 64
    syscall write,stdout,nosupport,nosupport.len
    syscall exit

    ; CPUID is supported
    xor     eax,eax                         ;get vendor ID
    mov     [output.regEBXvalue],ebx        ;first 4 letters of vendor from ebx in place
    mov     [output.regEDXvalue],edx        ;next 4 letters of vendor from edx in place
    mov     [output.regECXvalue],ecx        ;last 4 letters of vendor from ecx in place
    syscall write,stdout,output,output.len
    syscall exit