CPUID in Assembly

About

I wanted to learn some x86 assembly code, so I wrote a tiny program which reads the CPUID feature bits found on Intel CPUs and outputs this to the display. The code I have written, only works on Linux for x86(i386/i686 etc.) or x86_64(AMD64). The PC Assembly Book by Paul Carter helped me write this as did some Intel CPUID specification documents.

Download

Fork it on github
Executable
Assembly Source Code

How to Build from Source

Where I have used 'nasm', 'yasm' could also be used.

32bit x86 machines

nasm -f elf cpuid.asm
ld -s -o cpuid cpuid.o

64bit x86 machines

nasm -f elf cpuid.asm
ld -s -m elf_i386 -o cpuid cpuid.o

Info for non-Linux/x86 users

For those of you are still interested but don't have access to Linux/x86 system here is the assembly code I wrote (everything after a ; is a comment):

section .bss ; $Revision: 1.6 $
vendor_id       resd	12	;reserve 12 bytes of memory
version		resd	4
features	resd	4
i		resd	4
curfeat		resd	4


section .text
    global _start                       ;must be declared for linker (ld)
names	db	'FPU  VME  DE   PSE  TSC  MSR  PAE  MCE  CX8  APIC RESV SEP  MTRR PGE  MCA  CMOV PAT PSE3 PSN  CLFS RESV DS   ACPI MMX FXSR SSE  SSE2 SS   HTT  TM   RESV PBE '

_start:					;tell linker entry point

mov eax,0
cpuid
mov [vendor_id],ebx
mov [vendor_id+4],edx
mov [vendor_id+8],ecx

        ;;syscall(SYS_write, 0, str, sizeof(str)-1)
        mov     edx,12  ;message length
        mov     ecx,vendor_id   ;message to write (msg is a pointer to the start of the string

        mov     ebx,1   ;file descriptor (stdout)
        mov     eax,4   ;system call number (sys_write)
        int     0x80    ;call kernel


mov eax,1
cpuid
mov [version],eax
mov [features],edx
;mov [100000f0h],ebx ;break program for debugging

;;########################## A FOR LOOP ###################################################
;;for(i = 8; i != 0; i++){
mov	eax,00000001h
mov	[curfeat],eax
mov     eax,-1
mov     [i],eax
.loop:
;;--i
	mov     eax,[i] 
	inc eax         ;(i++)
	cmp     eax,31
	jz     .quitloop   ;quit loop if reached limit
	mov     [i],eax    ;put updated value on stack

;get current feature
        mov ebx,[curfeat]
;test for feature - if feature exists ebx is non zero
	and ebx,[features]
;left shift to test for next feature (will be used in next iteration of loop)
	mov eax,[curfeat]
        shl eax,1
        mov [curfeat],eax

;jump if feaure not exist
cmp ebx,0 
jz .loop ;check if zero flag is set - if it is it means that the feature didn't exist so we don't want to print anything out
;;otherwise this feature must exist lets print it out...
        mov     eax,[i] ;get value from stack           0x080480bf
        mov     edx,5   ;message length
        mov     ecx,names     ;message to write (msg is a pointer to the start of the string
times 5	add	ecx,eax
	
        mov     ebx,1   ;file descriptor (stdout)
        mov     eax,4   ;system call number (sys_write) 0x080480b7
        int     0x80    ;call kernel                    0x080480be

	jmp .loop ; unconditional jump
;}
.quitloop:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov	eax,1	;system call number (sys_exit)
	mov	ebx,0	;exit status 0 (if not used is 1 as set before) "echo $?" to check
	int	0x80	;call kernel
...and this is what output looks like on various processors:
Pentium 4 2.4Ghz
$ ./cpuid
GenuineIntelFPU  VME  DE   PSE  TSC  MSR  PAE  MCE  CX8  APIC SEP  MTRR PGE  MCA  CMOV PAT PSE3 PLFS RS   ACPI MMX FXSR SSE  SSE2 SS   HTT  TM   RE
Pentium 3 800Mhz
$ ~/scripts/ASM/cpuid
GenuineIntelFPU  VME  DE   PSE  TSC  MSR  PAE  MCE  CX8  APIC SEP  MTRR PGE  MCA  CMOV PAT PSE3 PMX FXSR SSE  SS
Celeron 300a Mhz
$ ./cpuid
GenuineIntelFPU  VME  DE   PSE  TSC  MSR  PAE  MCE  CX8  SEP  MTRR PGE  MCA  CMOV PAT PSE3 PMX FXSR SS