User:ChosenOreo/CPU Detection
Introduction
Since I first began developing my Operating System, I was concerned with not accurately detecting the system that the user is running on. I also was concerned with supporting the most hardware that I could. Thus, I went out in search of the best ways to detect various CPUs. These algorithms are documented here in this article, along with a brief description on where I got the information.
x86
Detecting the various kinds of CPUs in the x86 line can be very difficult at times, as there are an incredible selection of different CPUs, and there are some with very little differences or documentation.
The information in this section was obtained from a variety of sources. The first source is from Robert Collins, in his article CPUID Algorithm Wars.
Detecting 8086/88 and 80186/88 vs 80286+
There are many different ways to detect an 80286 and higher versus any CPUs that came before it. The two methods I've used are the stack pointer microcode bug method and the flags method.
Stack Pointer Microcode Bug
On the 8086/88 and 80186/88 CPUs, on a PUSH SP
operation, the stack pointer was modified before it was pushed to the stack. Thus, if you popped that value from the stack into another register and then compared it to the current stack pointer value, on a 80286+ they would be the same and on anything prior to it they would be different.
Here is some example code:
; In this code, if the CPU is determined to be an 8086/88 or 80186/88, the zero flag will be set.
push sp
pop bx
cmp bx, sp
jz Cpu_Is_8086_Or_80186
Flags Method
On the 8086/88 and 80186/88 CPUs, the flags register's bits 12-15 will always be set to 1. Thus, if we try to modify these values and they can be changed, then we must not have an 8086/88 or 80186/88.
Here is some example code:
; In this code, if the CPU is determined to be an 8086/88 or 80186/88, the zero flag will be set.
pushf
pop bx
mov ax, bx
and bh, 0x0f
push bx
popf
pushf
pop bx
and bh, 0xf0
cmp bh, 0xf0
push ax
popf
jz Cpu_Is_8086_Or_80186
8086/88 vs 80186/88
The 8086/88 and 80186/88 processors were very similar except for a few opcodes which can't be tested unless we have an invalid opcode handler (which is not present on the 8086/88 CPUs). Thus, we have to determine whether we have an 8086/88 or an 80186/88 a different way. We can do this by pushing a word at offset 0xffff in a segment. On an 8086/88, the bytes of this word will end up at offsets 0xffff and 0x0000, while on an 80186 the word will end up at bytes 0xffff and 0x10000.
Here is some example code:
; In this code, if the CPU is determined to be an 8086/88, the zero flag will be set.
mov ax, ds:[0x0000]
mov bx, ds:[0xffff]
mov byte ds:[0x0000], 0x00
mov word ds:[0xffff], 0xffff
cmp byte ds:[0x0000], 0xff
mov ds:[0x0000], ax
mov ds:[0xffff], bx
jz Cpu_Is_8086_Or_8088
x86 vs x88
On both the 8086 and 80186, the prefetch queue is 6 bytes long. On the 8088 and 80188, it is only 4 bytes long. Thus, if we modify a value 5 bytes ahead of our current location, only the 8088 or 80188 processors should detect it.
Here is some example code:
; In this code, if the CPU is determined to be an x88, the zero flag will be set.
mov ax, cs
mov es, ax
std
mov dx, 0x0001
mov di, WriteLocation
mov al, 0x90
mov cx, 0x0003
rep stosb
cld
nop
nop
nop
dec dx
nop
WriteLocation:
nop
test dx, dx
jz Cpu_Is_x88
Intel 8086/88 and 80186/88 vs NEC V20/V30 and V40/V50
This is questioned! - On the Intel processors, during a multiply operation, the zero flag is always cleared. On the NEC processors, the zero flag is set according to the result. Thus, to test for the NEC, we simply multiply something by zero and then check the zero flag.
Here is some example code:
; In this code, if the CPU is determined to be a NEC CPU, the zero flag will be set.
xor al, al
mov al, 0x40
mul al
jz Cpu_Is_NEC