   #Start Contents

                         Rozpoznawanie typu procesora

   (przeskocz wykrywanie procesora)

   Jak zapewne wiecie, wiele programow (systemy operacyjne, gry, ...)
   potrafi jakos dowiedziec sie, na jakim procesorze zostaly uruchomione.
   Rozpoznanie typu procesora umozliwia na przyklad uruchomienie
   dodatkowych optymalizacji w programie lub odmowe dalszego dzialania,
   jesli program musi korzystac z instrukcji niedostepnych na danym
   procesorze.
   Wykrywanie rodzaju CPU i FPU nie jest trudne i pokaze teraz, jak po
   kolei sprawdzac typ procesora (nie mozna przeciez zaczac sprawdzania
   od najwyzszych).
   Informacje, ktore tutaj podam, sa oczywiscie sluszne dla wszystkich
   procesorow rodziny x86 (AMD, Cyrix, ...), a nie tylko Intela.

   Generalnie sposob wykrywania pod Linuksem jest jeden (nie wliczajac
   zadnych dodatkowych funkcji jadra czy tez czytania z /proc/cpuinfo):
   poprzez rejestr E-FLAG.
    1. 80386
       (przeskocz 80386)
       Na tym procesorze nie mozna zmienic bitu numer 18 we flagach
       (wiemy, ze rejestr flag ma 32 bity). Bit ten odpowiada za
       Alignment Check i spowoduje przerwanie m.in wtedy, gdy SP nie
       bedzie podzielne przez 4. Dlatego, zanim bedziemy testowac ten
       bit, musimy zachowac SP i wyzerowac jego najmlodsze 2 bity.
       (przeskocz kod dla 80386)
                mov     dx, sp
                and     sp, ~3          ; aby uniknac 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? jesli tak, to 386
                mov     sp, dx          ; przywrocenie SP
                jz      jest_386
    2. 80486
       (przeskocz 80486)
       Na tym procesorze nie mozna zmienic bitu 21 we flagach. Jesli ten
       bit mozna zmienic, to procesor obsluguje instrukcje CPUID, ktorej
       bedziemy uzywac 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? jesli tak, to 486
                jz      jest_486
                jmp     jest_586

   Zanim omowie sposob korzystania z instrukcji CPUID, zajmijmy sie
   sposobem rozpoznania typu koprocesora.
     _________________________________________________________________

Koprocesor

   (przeskocz wykrywanie koprocesora)

   Tutaj mozliwosci sa tylko 4: brak koprocesora, 8087, 80287, 80387. No
   to do roboty.
    1. czy w ogole jest jakis koprocesor?
       (przeskocz test na istnienie FPU)
       To sprawdzamy bardzo latwo. Jesli nie ma koprocesora, to w chwili
       wykonania instrukcji FPU moze wystapic przerwanie INT6
       (nieprawidlowa instrukcja), ale nie o tym sposobie chcialem
       powiedziec. Koprocesor mozna wykryc, jesli slowo stanu zostanie
       zapisane prawidlowo. Oto kod:
       (przeskocz test na istnienie FPU)
                fninit                  ; inicjalizacja zeruje rejestry

                ; wpisujemy jakas niezerowa wartosc:
                mov     word [_fpu_status], 5a5ah

                ; zapisz slowo statusowe do pamieci:
                fnstsw  [_fpu_status]
                mov     ax, [_fpu_status]
                or      al, al  ; jesli zapisalo dobrze (zera oznaczaja
                                ; puste rejestry), to jest FPU

                jz      jest_FPU
    2. 8087
       (przeskocz 8087)
       Sztuczka polega na wykorzystaniu instrukcji FDISI (wylaczenie
       przerwan), ktora rzeczywiscie cos robi tylko na 8087. Po
       wylaczeniu przerwan w slowie kontrolnym zostaje wlaczony bit numer
       7.
       (przeskocz kod dla 8087)
                ; zachowaj slowo kontrolne do pamieci:
                fnstcw  [_fpu_status]

                ; wylaczamy wszystkie
                ; przerwania (poprzez slowo kontrolne):
                and     word [_fpu_status], 0ff7fh

                ; zaladuj slowo kontrolne z pamieci:
                fldcw   [_fpu_status]

                fdisi           ; wylaczamy wszystkie przerwania
                                ; (jako instrukcja)

                ; zachowaj slowo kontrolne do pamieci:
                fstcw   [_fpu_status]
                test    byte [_fpu_status], 80h ; bit 7 ustawiony?

                jz      nie_8087        ; jesli nie, to nie jest to 8087
    3. 80287
       (przeskocz 80287)
       Koprocesor ten nie odroznia minus nieskonczonosci od plus
       nieskonczonosci. Kod na sprawdzenie tego wyglada tak:
       (przeskocz kod dla 80287)
                finit

                fld1            ; st(0)=1
                fldz            ; st(0)=0,st(1)=1
                fdivp   st1     ; tworzymy nieskonczonosc,
                                ; dzielac przez 0
                fld     st0     ; st(1):=st(0)=nieskonczonosc
                fchs            ; st(0)= minus nieskonczonosc

                                ; porownanie st0 z st1 i
                                ; zdjecie obu ze stosu
                fcompp          ; 8087/287: -niesk. = +niesk.,
                                ; 387: -niesk. != +niesk.

                fstsw   [_fpu_status]   ; zapisz status do pamieci
                mov     ax, [_fpu_status]       ; AX = status

                sahf            ; zapisz AH we flagach. tak sie sklada,
                                ; ze tutaj rowniez flaga ZF wskazuje na
                                ; rownosc argumentow.
                jz      jest_287
                jmp     jest_387
     _________________________________________________________________

Dalsze informacje o procesorze - instrukcja CPUID

   Od procesorow 586 (choc niektore 486 tez podobno ja obslugiwaly),
   Intel i inni wprowadzili instrukcje CPUID. Pozwala ona odczytac wiele
   roznych informacji o procesorze (konkretny typ, rozmiary pamieci
   podrecznych, dodatkowe rozszerzenia, ...).
   Korzystanie z tej instrukcji jest bardzo proste: do EAX wpisujemy
   numer (0-3) i wywolujemy instrukcje, na przyklad
        mov     eax, 1
        cpuid

   Teraz omowie, co mozna dostac przy roznych wartosciach EAX.
    1. EAX=0
       (przeskocz EAX=0)
       EAX = maksymalny numer funkcji dla CPUID.
       EBX:EDX:ECX = marka procesora (12 znakow ASCII).
       Intel - "GenuineIntel"
       AMD - "AuthenticAMD"
       NexGen - "NexGenDriven"
       Cyrix, VIA - "CyrixInstead"
       RISE - "RiseRiseRise",
       Centaur Technology/IDT - "CentaurHauls" (programowalne, moze byc
       inne)
       United Microelectronics Corporation - "UMC UMC UMC "
       Transmeta Corporation - "GenuineTMx86"
       SiS - "SiS SiS SiS "
       National Semiconductor - "Geode by NSC".
    2. EAX=1
       (przeskocz EAX=1)
       EAX = informacje o wersji:
          + bity 0-3: stepping ID
          + bity 4-7: model
          + bity 8-11: rodzina. Wartosci moga byc od 4 (80486) do 7
            (Itanium) oraz 15 (co znaczy "sprawdz rozszerzone informacje
            o rodzinie")
          + bity 12-13: typ procesora (0=Original OEM Processor, 1=Intel
            Overdrive, 2=Dual)
          + bity 16-19 (jesli jest taka mozliwosc): rozszerzona
            informacja o modelu.
          + bity 20-27 (jesli jest taka mozliwosc): rozszerzona
            informacja o rodzinie.
       EDX = cechy procesora (tutaj akurat z procesorow Intela; najpierw
       numery bitow):
          + 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
            Przerwan (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 mozliwosc modyfikacji
            wydajnosci 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, uklady kontroli temperatury
          + 31: Pending Break Enable
    3. EAX=2
       EBX, ECX, EDX = informacje o pamieci podrecznej cache i TLB

   Nawet te informacje, ktore tu przedstawilem sa juz bardzo szczegolowe
   i z pewnoscia nie beda takie same na wszystkich procesorach. To jest
   tylko wstep. Dalsze informacje mozna znalezc na stronach producentow
   procesorow, na przyklad AMD, Intel, ale takze tutaj: Sandpile, Lista
   przerwan Ralfa Brown'a (plik opcodes.lst).

   Spis tresci off-line (klawisz dostepu 1)
   Spis tresci on-line (klawisz dostepu 2)
   Ulatwienia dla niepelnosprawnych (klawisz dostepu 0)
