how can I got the CPU's serial number.
you must care different CPU first.
80380, 80486, P, PII, PIII and other none intel CPU.
any help is welcome
by Chris Dragan & Chili
Being able to identify the processor in which your program is running, can be a
very useful feature, if not to ensure that your program will work on a wider
range of computers, at least to provide minimum compatibility and guarantee it
not to crash on some processors.
The first part of this article explains how to distinguish between older 80486
and lower processors by checking for known behaviours, while the second part
(written by Chris) takes it one step forward, explaining how to use the CPUID
instruction on newer processors, checking the ID register by means of a TFR and
how to correctly identify a Cyrix processor.
On old pre-286 CPUs, bits 12 through 15 of the FLAGS register are always set,
so we can check for this type of processor, in opposition to newer ones, by
attempting to clear those bits:
and ax, 0fffh ; clear bits 12-15
and ax, 0f000h
cmp ax, 0f000h ; check if bits 12-15 are set
Once we know that we are at least on a 286 processor, we can then check to see
if we're on a 32-bit processor (386 or higher) or on an actual 286. For this
purpose we know that bits 12-15 of the FLAGS register are always clear on a 286
processor in real mode:
or ax, 0f000h ; set bits 12-15
and ax, 0f000h ; check if bits 12-15 are clear
If instead, the processor is running in protected mode these bits are used for
the IOPL (bits 12-13) and NT (bit 14) flags. Note that bits 12-14 hold the last
value loaded into them on 32-bit processors in real mode. Also remember that
there is no virtual-8086 mode on 16-bit processors.
In order to find out if the processor is in real or protected mode we must test
if the Protection Enable flag (bit 0 of CR0) is set, if so then we're in
and ax, 0001h ; check if bit 0 (PE) is clear
To find out if it is a 486 or a newer processor we'll try to set the AC flag
(bit 18), since it is always clear on a 386 processor (also NexGen Nx586),
unlike newer ones that allow it to be toggled:
xor eax,40000h ; toggle bit 18
xor eax,ebx ; check if bit 18 changed
And finally to check if we're in an old 486 or in a new 486 and other newer
processors (i.e. Pentium), we'll try to toggle the ID flag (bit 21) which
indicates the presence of a processor that supports the CPUID instruction. This
part is explained below in a section about CPUID.
PUSH SP Instruction
Before the 286, processors implemented the "PUSH SP" instruction in a different
way, updating the stack pointer before the value of SP is pushed onto the
stack, unlike newer processors which push the value of the SP register as it
existed before the instruction was executed (both in real and virtual-8086
Older CPUs 286+
SP = SP - 2 TEMP = SP
SS:SP = SP SP = SP - 2
} SS:SP = TEMP
(credit for the PUSH SP algorithm representation goes to Robert Collins)
So all one has to do is see if the values of the SP register are different
before and after the PUSH SP:
cmp ax, sp ; check if SP values differ
Note - If you want the same result on all processors, use the following code
instead of a PUSH SP instruction:
mov bp, sp
xchg bp, [bp]
Shift and Rotate Instructions
Starting with the 186/88, all processors mask shift/rotate counts by modulo 32,
restricting the maximum count to 31 (in all operating modes, including the
virtual-8086 mode). Earlier CPUs do not mask the shift/rotation count, using
all 8-bits of CL. So, if we try to perform a 32-bit shift, on newer processors
we'll end up with the same result (since the shift count is masked to 0),
whereas on an older processor the result will be zero:
mov ax, 0ffffh
mov cl, 32
shl ax, cl ; check if result is zero
NEC processors differ from Intel's with respect to the handling of the zero
flag (ZF) during a MUL operation. While a NEC V20/V30 does not clear ZF after a
non-zero multiplication result, but only according to it, an Intel 8086/88 will
always clear it (note that this is only true for the specified processors):
xor al, al ; force ZF to set
mov al, 40h
mul al ; check if ZF is clear
In addition to the list of sites where you can find more information, provided
by Chris at the end of this article, you can also try this one:
http://grafi.ii.pw.edu.pl/gbm/x86/ (Grzegorz Mazur)
And also the following packages/programs (available somewhere in the net):
The Undocumented PC (Frank van Gilluwe)
HelpPC (David Jurgens)
80x86.CPU file (Christian Ludloff)
Beginning with the 80386 processor, Intel included a so-called ID register,
which contains information about the processor model and stepping. This
register is accessible in an unusual way - it is passed in DX after reset.
To read the ID register one must proceed the following steps:
1. By storing value 0Ah (resume with jump) at address 0Fh (reset code) in the
CMOS data area, inform BIOS not to issue POST after reset, but to return
the control to the program.
2. Update after-reset-far-jump address at 0040h:0067h.
3. Set shutdown status word (0040h:0072h) to 0, to avoid undesirable
4. Cause a reset.
Causing a reset is typically done by issuing a so-called triple-fault-reset,
i.e. causing an error from which the processor cannot recover and enters
a reset state. TFR (triple...) can be done only if we have enough control
over the processor, i.e. under plain DOS in real mode (no EMS) or under
Win'95 (this is risky). The following code shows how to do it in DOS. The code
is assumed to be in a COM program.
GDT dd 0, 0 ; Selector 0 is empty
dd 0000FFFFh, 00009A00h ; Selector 8 - code segment
GDTR dw 000Fh, 0, 0 ; Limit 0Fh - two selectors
IDTR dw 0, 0, 0 ; Empty IDT will cause TFR
; Ensure that we are in real mode, not in V86
and al, 1
jnz near _skip_tfr_since_in_v86_mode
; Update code descriptor as we are going to enter pmode
xor eax, eax
mov ax, cs
shl eax, 4
or [GDT+10], eax
add eax, GDT
mov [GDTR+2], eax
; Update reset code in CMOS data area
cli ; Disable interrupts
mov [SaveSP], sp ; Save stack pointer
mov al, 0Fh ; Address 0Fh in CMOS area
out 70h, al
times 3 jmp short $+2 ; Short delay
mov al, 0Ah ; Value 0Ah - far jump
out 71h, al
; Update resume address
push word 0
mov [es:0467h], word _tfr ; offset
mov [es:0469h], cs ; segment
mov [es:0472h], word 0 ; Update shutdown status
; Switch to pmode
lgdt [GDTR] ; Load GDT
lidt [IDTR] ; Load empty IDT
or al, 01h ; Set pmode bit
jmp 0008h:_reset ; Reload CS
_reset: mov ax, [cs:0FFFFh] ; Reach beyond segment limit
; After reset we are here with DX containing the ID register
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, [SaveSP]
Of course there are also other ways of reading the ID register. They are well
described in DDJ (www.x86.org).
As said before, the ID register contains information about processor model and
stepping. The format of the register is as follows:
bits 15..12 - stepping
bits 11..8 - model
bits 7..0 - revision
Some example ID register values:
This format of the ID register was used in Intel 386 processors (all except
RapidCAD), AMD 386 processors and most of IBM 486 processors.
Another format of the ID register was introduced with Intel 486 processors.
This format is similar to the format of CPUID model information (see below),
and until the Pentium was kept the same. However newer processors do not keep
any useful information in the ID register (it is usually 0). This also concerns
Cyrix 486 processors.
bits 15..14 - unused, zero
bits 13..12 - typically indicate overdrive
bits 11..8 - model
bits 7..4 - stepping
bits 3..0 - revision
And some example ID register values with this format for Intel processors:
All Cyrix processors have a Device-Identification-Registers, which are used to
identify these processors. To read DIRs, one first has to determine that he
uses a Cyrix processor. This can be accomplished in two ways:
1. On modern processors using CPUID instruction.
2. On first Cyrix processors issuing 5/2 method.
If there is no CPUID instruction, one has to use the other way of
determination. If one knows that he is on a 486 processor, he can use the
mov ax, 0005h
mov cl, 2
cmp ah, 2
Once we have determined we are on a Cyrix processor, we can read its DIRs to
get its model and stepping information. All Cyrix processors have their special
registers accessible through ports 22h and 23h. Port 22h keeps register number
and port 23h register value.
; This function reads a Cyrix control register
; It expects a register address in AL and returns value also in AL
ReadCCR: out 22h, al ; select register
times 3 jmp short $+2 ; delay
in al, 23h ; get register contents
DIRs have offsets 0FEh (DIR1) and 0FFh (DIR0). DIR1 contains revision, while
DIR0 contains model/stepping. The following code reads them:
mov al, 0FEh
mov [DIR1], al
mov al, 0FFh
mov [DIR0], al
Example DIR0 values:
31 6x86(L) clock x2
55 6x86MX clock x4
All newer processors have the CPUID instruction, which helps to identify on
what processor we are. Before using it, we must first determine if it is
supported, by flipping the ID flag (bit 21 of EFLAGS).
xor eax, 00200000h ; flip bit 21
xor eax, ecx ; check if bit 21 was flipped
The only problem may be that NexGen processors do not support the ID flag, but
they do support the CPUID instruction. To determine that, we must hook Invalid
Opcode exception (int6) and execute the instruction. If the exception is
triggered, CPUID is not supported.
Also some early Cyrix processors (namely 5x86 and 6x86) have the CPUID
instruction disabled. To enable it, we must first enable extended CCRregisters
and then enable the instruction, setting bit 7 in CCR4.
; Enable extended CCRs
mov al, 0C3h ; C3 corresponds to CCR3
and ah, 0Fh ; bits 7..4 of CCR3 <- 0001b
or ah, 10h
; Enable CPUID
mov al, 0E8h ; E8 corresponds to CCR4
or ah, 80h ; bit 7 enables CPUID
The following functions are used to read/write CCRs:
ReadCCR: out 22h, al ; Select control register
times 3 jmp short $+2
xchg al, ah
in al, 23h ; Read the register
xchg al, ah
WriteCCR: out 22h, al ; Select control register
times 3 jmp short $+2
mov al, ah
out 23h, al ; Write the register
After enabling CPUID we must test if it is supported by flipping the ID flag,
unless of course we have determined that we are not on a 5x86 or 6x86 by
Once we have determined that CPUID is supported, we can use it to identify the
processor. The instruction expects EAX to hold a function number and returns
information corresponding to this number in EAX, ECX,EDX and EBX. The two most
important levels are listed below.
level 0 (eax=0) returns:
eax Maximum available level
ebx:edx:ecx Vendor ID in ASCII characters
Intel - "GenuineIntel" (ebx='Genu', bl='G'(47h))
AMD - "AuthenticAMD"
Cyrix - "CyrixInstead"
Rise - "RiseRiseRise"
Centaur - "CentaurHauls"
NexGen - "NexGenDriven"
UMC - "UMC UMC UMC "
level 1 (eax=1) returns:
eax bits 13..12 0 - normal
1 - overdrive
2 - secondary in dual system
bits 11..8 model
bits 7..4 stepping
bits 3..0 revision
If Processor Serial Number is enabled, all 32
bits are treated as the high bits (95..64) of
edx Processor features (e.g. bit 23 indicates MMX)
There are also other levels, i.e. level 2 returns cache and TLB descriptors,
level 3 the rest of Processor Serial Number.
Other processors (AMD, Cyrix) also support extended levels. The first extended
level is 80000000h and it returns in EAX the maximum extended level. These
extended levels return information specific to that processors, e.g. 3DNow!
support or processor name.
This example code determines MMX support:
; First check maximum available level
xor eax, eax ; eax = 0 (level 0)
cmp eax, 0
; Now check MMX support
mov eax, 1 ; level 1
test edx, 00800000h ; bit 23 is set if MMX is supported
As this is not the place for listing all the available information about what
values are returned by CPUID, ID register or DIRs, you should get the most
recent information from the processor vendors:
Also you can find very valuable information about the identification topic on: