Asembler: DOS: Rozpoznawanie typu procesora
Rozpoznawanie typu procesora
(przeskocz wykrywanie procesora)
Jak zapewne wiecie, wiele programów (systemy operacyjne, gry, ...) potrafi jakoś
dowiedzieć się, na jakim procesorze zostały uruchomione. Rozpoznanie typu procesora
umożliwia np. uruchomienie dodatkowych optymalizacji w programie lub odmowę dalszego
działania, jeśli program musi korzystać z instrukcji niedostępnych na danym procesorze.
Wykrywanie rodzaju
CPU i
FPU nie jest trudne i
pokażę teraz, jak po kolei sprawdzać
typ procesora (nie można przecież zacząć sprawdzania od najwyższych).
Informacje, które tutaj podam, są oczywiście słuszne dla wszystkich procesorów
rodziny x86 (AMD, Cyrix, ...),
a nie tylko Intela.
Generalnie sposoby wykrywania są dwa: poprzez rejestr FLAG lub poprzez zakodowanie w kodzie
instrukcji, które wykonają się tylko na danym modelu (i późniejszych).
Drugi sposób jest trochę trudniejszy:
należy przejąć przerwanie INT6 (nieprawidłowa instrukcja) i sprawdzać, kiedy zostało wywołane.
odróżnienie 8088 od reszty
(przeskocz 8088)
Procesor 8088 od pozostałych odróżnia to, że zmniejsza on rejestr SP przed umieszczeniem
go na stosie. Reszta robi to po umieszczeniu SP na stosie. Kod wyglądałby więc na przykład tak:
(przeskocz kod dla 8088)
mov ax, sp
push sp
pop cx
xor ax, cx ; lub cmp ax, cx
jz nie_8088
8086
(przeskocz 8086)
Na tym procesorze w rejestrze flag bity 12-15 zawsze mają wartość 1.
(przeskocz kod dla 8086)
pushf ; flagi na stos
pop ax ; AX = flagi
and ax, 0fffh ; czyścimy bity 12-15
push ax ; AX na stos
popf ; flagi = AX
pushf ; z powrotem na stos
pop ax ; AX = flagi
and ax, 0f000h ; zerujemy bity poza bitami 12-15
cmp ax, 0f000h ; jeśli ustawione, to 8086
jz jest_8086
80186
(przeskocz 80186)
Test polega na próbie wykonania instrukcji smsw dx, nieprawidłowej
na procesorach wcześniejszych
niż 80286. Przerwanie nieprawidłowej instrukcji przejmujemy tak:
(przeskocz kod dla 80186)
xor ax, ax
mov es, ax
les bx, [es:6 << 2] ; FASM: les bx, [es:(6 shl 2)]
mov [_stare06+2], es
mov [_stare06], bx
mov es, ax
mov word [es:(6 << 2)], moje06
; FASM: mov word [es:(6 shl 2)], moje06
mov word [es:(6 << 2) + 2], seg moje06
; FASM: mov word [es:(6 shl 2)], seg moje06
Sama procedura obsługi przerwania wyglądać będzie tak:
moje06:
pop ax
add ax, 3
push ax
xor ax, ax
iret
Proste: zwiększamy adres powrotny o 3 (długość instrukcji smsw dx) i zerujemy AX
(potem w kodzie sprawdzimy jego wartość). Sam kod sprawdzający wygląda tak:
mov ax, 1
db 0fh, 1, 0e2h ; smsw dx
or ax, ax
jz jest_286
Przywrócenie oryginalnej procedury wygląda tak:
xor ax, ax
les cx, [_stare06]
mov ds, ax
mov [ds:(6 << 2)], cx
; FASM: mov [ds:(6 shl 2)], cx
mov [ds:(6 << 2) + 2], es
; FASM: mov [ds:(6 shl 2) + 2], es
80286
(przeskocz 80286)
Na tym procesorze bity 12-15 flag zawsze mają wartość 0. Przykładowy kod wygląda więc tak:
(przeskocz kod dla 80286)
pushf ; flagi na stos
pop ax ; AX = flagi
or ax, 0f000h ; ustawiamy bity 12-15
push ax ; AX na stos
popf ; flagi = AX
pushf ; flagi na stos
pop ax ; AX = flagi
and ax, 0f000h ; jeśli wyczyszczone, to 286
jnz nie_286
80386
(przeskocz 80386)
Na tym procesorze nie można zmienić bitu numer 18 we flagach (wiemy, że rejestr flag ma 32 bity).
Bit ten odpowiada za Alignment Check i spowoduje przerwanie m.in wtedy, gdy SP nie będzie podzielne przez 4.
Dlatego, zanim będziemy testować ten bit, musimy zachować SP i wyzerować jego najmłodsze 2 bity.
(przeskocz kod dla 80386)
mov dx, sp
and sp, ~3 ; aby uniknąć AC fault.
; FASM: and sp, not 3
pushfd ; flagi na stos
pop eax ; EAX = E-flagi
mov ecx, eax ; zachowanie EAX
xor eax, 40000h ; zmiana bitu 18
push eax ; EAX na stos
popfd ; E-flagi = EAX
pushfd ; flagi na stos
pop eax ; EAX = flagi
xor eax, ecx ; czy takie same? jeśli tak, to 386
mov sp, dx ; przywrócenie SP
jz jest_386
80486
(przeskocz 80486)
Na tym procesorze nie można zmienić bitu 21 we flagach. Jeśli ten bit można zmienić, to
procesor obsługuje instrukcję CPUID, której będziemy używać do dalszego rozpoznania. Kod:
(przeskocz kod dla 80486)
pushfd ; flagi na stos
pop eax ; EAX = E-flagi
mov ecx, eax ; zachowanie EAX
xor eax, 200000h ; zmiana bitu 21
push eax ; EAX na stos
popfd ; E-flagi = EAX
pushfd ; flagi na stos
pop eax ; EAX = flagi
xor eax, ecx ; czy takie same? jeśli tak, to 486
jz jest_486
jmp jest_586
Zanim omówię sposób korzystania z instrukcji CPUID,
zajmijmy się sposobem rozpoznania typu koprocesora.
Koprocesor
(przeskocz wykrywanie koprocesora)
Tutaj możliwości są tylko 4: brak koprocesora, 8087, 80287, 80387. No to do roboty.
czy w ogóle jest jakiś koprocesor?
(przeskocz test na istnienie FPU)
To sprawdzamy bardzo łatwo. Jeśli nie ma koprocesora, to w chwili wykonania instrukcji FPU może wystąpić przerwanie INT6
(nieprawidłowa instrukcja), ale nie o tym sposobie chciałem powiedzieć. Koprocesor można wykryć,
jeśli słowo stanu zostanie zapisane prawidłowo. Oto kod:
(przeskocz test na istnienie FPU)
fninit ; inicjalizacja zeruje rejestry
; wpisujemy jakąś niezerowa wartość:
mov word [_fpu_status], 5a5ah
; zapisz słowo statusowe do pamięci:
fnstsw [_fpu_status]
mov ax, [_fpu_status]
or al, al ; jeśli zapisało dobrze (zera oznaczają
; puste rejestry), to jest FPU
jz jest_FPU
8087
(przeskocz 8087)
Sztuczka polega na wykorzystaniu instrukcji FDISI (wyłączenie przerwań), która rzeczywiście coś robi tylko na 8087.
Po wyłączeniu przerwań w słowie kontrolnym zostaje włączony bit numer 7.
(przeskocz kod dla 8087)
; zachowaj słowo kontrolne do pamięci:
fnstcw [_fpu_status]
; wyłączamy wszystkie
; przerwania (poprzez słowo kontrolne):
and word [_fpu_status], 0ff7fh
; załaduj słowo kontrolne z pamięci:
fldcw [_fpu_status]
fdisi ; wyłączamy wszystkie przerwania
; (jako instrukcja)
; zachowaj słowo kontrolne do pamięci:
fstcw [_fpu_status]
test byte [_fpu_status], 80h ; bit 7 ustawiony?
jz nie_8087 ; jeśli nie, to nie jest to 8087
80287
(przeskocz 80287)
Koprocesor ten nie odróżnia minus nieskończoności od plus nieskończoności. Kod na sprawdzenie tego wygląda tak:
(przeskocz kod dla 80287)
finit
fld1 ; st(0)=1
fldz ; st(0)=0,st(1)=1
fdivp st1 ; tworzymy nieskończoność,
; dzieląc przez 0
fld st0 ; st(1):=st(0)=niesk.
fchs ; st(0)= -niesk.
; porównanie st0 z st1 i
; zdjęcie obu ze stosu
fcompp ; 8087/287: -niesk. = +niesk.,
; 387: -niesk. != +niesk.
fstsw [_fpu_status] ; zapisz status do pamięci
mov ax, [_fpu_status] ; AX = status
sahf ; zapisz AH we flagach. tak sie składa,
; że tutaj również flaga ZF wskazuje na
; równość argumentów.
jz jest_287
jmp jest_387
Dalsze informacje o procesorze - instrukcja
CPUID
Od procesorów 586 (choć niektóre 486 też podobno ją obsługiwały), Intel i inni wprowadzili
instrukcję CPUID. Pozwala ona odczytać wiele różnych
informacji o procesorze (konkretny typ,
rozmiary pamięci podręcznych, dodatkowe rozszerzenia, ...).
Korzystanie z tej instrukcji jest bardzo proste: do EAX wpisujemy numer (0-3)
i wywołujemy instrukcję, np.
mov eax, 1
cpuid
Teraz omówię, co można dostać przy różnych wartościach EAX.
EAX=0
(przeskocz EAX=0)
EAX = maksymalny numer funkcji dla CPUID.
EBX:EDX:ECX = marka procesora (12 znaków ASCII).
Intel - GenuineIntel
AMD - AuthenticAMD
NexGen - NexGenDriven
Cyrix, VIA - CyrixInstead
RISE - RiseRiseRise,
Centaur Technology/IDT - CentaurHauls (programowalne, może być inne)
United Microelectronics Corporation - UMC UMC UMC
Transmeta Corporation - GenuineTMx86
SiS - SiS SiS SiS
National Semiconductor - Geode by NSC.
EAX=1
(przeskocz EAX=1)
EAX = informacje o wersji:
bity 0-3: stepping ID
bity 4-7: model
bity 8-11: rodzina. Wartości mogą być od 4 (80486) do 7 (Itanium) oraz 15
(co znaczy sprawdź rozszerzone informacje o rodzinie)
bity 12-13: typ procesora (0=Original OEM Processor,
1=Intel Overdrive, 2=Dual)
bity 16-19 (jeśli jest taka możliwość): rozszerzona informacja o modelu.
bity 20-27 (jeśli jest taka możliwość): rozszerzona informacja o rodzinie.
EDX = cechy procesora (tutaj akurat z procesorów Intela; najpierw numery bitów):
0: procesor zawiera FPU
1: Virtual 8086 Mode Enchancements
2: Debugging Extensions
3: Page Size Extension
4: Time Stamp Counter
5: Model Specific Registers
6: Physical Address Extensions
7: Machine Check Exception
8: instrukcja CMPXCHG8B
9: procesor zawiera Zaawansowany Programowalny Kontroler Przerwań (APIC)
11: instrukcje SYSENTER i SYSEXIT
12: Memory Type Range Registers
13: Page Table Entries Global Bit
14: Machine Check Architecture
15: instrukcje CMOV*
16: Page Attribute Table
17: 32-bit Page Size Extensions
18: numer seryjny procesora
19: instrukcja CLFLUSH
21: Debug Store
22: monitorowanie temperatury i możliwość modyfikacji wydajności procesora
23: technologia MMX
24: instrukcje FXSAVE i FXRSTOR
25: technologia SSE
26: technologia SSE2
27: Self-Snoop
28: technologia Hyper-Threading
29: monitorowanie temperatury, układy kontroli temperatury
31: Pending Break Enable
EAX=2
EBX, ECX, EDX = informacje o pamięci podręcznej cache i TLB
Nawet te informacje, które tu przedstawiłem są już bardzo szczegółowe i z pewnością nie będą
takie same na wszystkich procesorach. To jest tylko wstęp. Dalsze informacje można znaleźć
na stronach producentów procesorów, np.
AMD,
Intel,
ale także tutaj:
Sandpile,
Lista przerwań Ralfa Brown'a (plik opcodes.lst)
Spis treści off-line (Alt+1)
Spis treści on-line (Alt+2)
Ułatwienia dla niepełnosprawnych (Alt+0)
Wyszukiwarka
Podobne podstrony:
CPU TUTDOS CPU TUTCPU TUTcpudo pomiary temperatury cpu ReadMe!ART121 tut 2phys tut 08czujnik obrotow went CPUphys tut 12DOS DIOD TUTepox cpu supportSYS TUTART121 tut 3MYSZ TUTPWR TUTwięcej podobnych podstron