wtorek, 1 kwietnia 2014

Architektura procesorów rodziny IA-32 (x86-32) oraz Intel 64 (x86-32e)

Do wymienionej w temacie dużej rodziny procesorów zaliczamy wszystkie procesory rodziny Celeron, Pentium i Core. Współczesne procesory x86-32 począwszy od roku 2003 (niektóre model Pentium 4 oraz wszystkie następne takie jak Core 2 czy Core i7) posiadają dodatkowy 64-bitowy tryb pracy. Architekturę 64-bitową wprowadziła po raz pierwszy AMD pod nazwą x86-64. 64-bitowe rozszerzenie architektury procesorów Intel oznaczane jest jako x86-32e tudzież EM64T. Obecnie oficjalnie jest używana nazwa Intel 64, której nie należy mylić z IA-64, w oparciu o którą zbudowana jest zupełnie inna linia procesorów opracowanych przez firmy Hewlett-Packard oraz Intel o nazwie Itanum.

Każy kolejny procesor firmy Intel z linii x86-32 był tak projektowany aby oprogramowanie działające na starszym modelu mogło być w dalszym ciągu używane.

Jeżeli chodzi o procesory AMD to zbiór instrukcji jednostki stałoprzecinkowej oraz zmiennoprzecinkowej (procesora głównego oraz koprocesora arytmetycznego) jest zgodny z firmą Intel. Podobnie jest z instrukcjami MMX poczynając od procesora AMD K6 2. W kolejnych procesorach K6 firma wprowadziła swoje rozszerzenie technologii MMX o nazwie 3DNow! bazujące na 64-bitowych rejestrach MMX. Można powiedzieć, że była to odpowiedź na SSE Intela. Instrukcje 3DNow! pozwalają wykonywać operacje na dwóch spakowanych wartościach zmiennoprzecinkowych pojedynczej precyzji.  Kolejnym rozszerzeniem 3DNow!, czasem nazywanym 3DNow+, jest DSP. Intelowskie rozszerzenie SSE posiada AMD Athlon 4 i nowsze, a SSE2 pojawiło się w AMD Athlon 64.

Procesor 8086 Intela był pierwszym, w którym rejestry dostępne programowo są 16-bitowe. Z niewielkim uproszczeniem można przyjąć, że procesor o architekturze x86-32 widziany jest przez programistę jako szybki procesor 8086. Można stwierdzić, iż procesor pracuje wtedy trybie adresacji rzeczywistej bądź w trybie 16 bitów (chodź programowo są dostępne rejestry 32-bitowe wprowadzone w procesorze 80386). Instrukcje procesora 8086 stanowią podzbiór zbioru instrukcji np. procesora Intel Core i7. Programy mogą operować na ośmiu podstawowych rejestrach:
  • AX (16 bitów): główny rejestr jednostki stałoprzecinkowej procesora, który służy jako akumulator dla argumentów instrukcji procesora oraz zapamiętania wyników:
    • AH (8 bitów),
    • AL (8 bitów),
  • BX (16 bitów): rejestr bazowy będący wskaźnikiem do danych w pamięci (w segmencie danych):
    • BH (8 bitów),
    • BL (8 bitów),
  • CX (16 bitów): licznik w operacjach na łańcuchach oraz w pętlach programowych:
    • CH (8 bitów),
    • CL (8 bitów),
  • DX (16 bitów): rejestr danych będący rejestrem adresowym układów wejścia i wyjścia:
    • DH (8 bitów),
    • DL (8 bitów),
  • SI (16 bitów): rejestr indeksowy stanowiący wskaźnik do danych (w operacjach na łańcuchach wskaźnik dla łańcucha źródłowego),
  • DI (16 bitów): rejestr indeksowy stanowiący wskaźnik do danych (w operacjach na łańcuchach wskaźnik dla łańcucha przeznaczenia),
  • BP (16 bitów): rejestr bazowy jako wskaźnik do danych w segmencie stosu,
  • SP (16 bitów): wskaźnik szczytu stosu.

Należy wymienić jeszcze dwa inne rejestry procesora 8086:
  • IP (16 bitów): wskaźnik instrukcji wskazujący adres w segmencie kodu, z którego kolejno pobierane będą instrukcje programu do wykonania,,
  • FLAGS (16 bitów): rejestr znaczników:
    • znaczniki stanu:
      • CF (1 bit): znacznik przeniesienia (przyjmuje 1 gdy w wyniku wykonanej operacji nastąpiło przeniesienie z bitu najstarszego na zewnątrz lub też nastąpiła pożyczka z zewnątrz do bitu najstarszego,
      • PF (1 bit): znacznik parzystości (ustawiany na 1 gdy w wyniku realizowanej operacji liczba bitów o wartości 1 w młodszym bajcie wyniku jest parzysta,
      • AF (1 bit): znacznik przeniesienia pomocniczego (przyjmuje wartość 1 gdy nastąpiło przeniesienie z bitu 3 na 4 lub pożyczka z bitu 4 na 3),
      • ZF (1 bit): znacznik zera (przyjmuje wartość 1 gdy wynik operacji jest równy zeru),
      • SF: znacznik znaku (1 gdy najbardziej znaczący bit czyli bit znaku w otrzymywanym wyniku jest równy 1),
      • OF: znacznik przepełnienia (przyjmuje wartość 1 gdy przy realizowaniu określonej operacji wystąpiło przeniesienie na bit znaku lub też z tego bitu pobrana została pożyczka ale nie wystąpiło przeniesienie lub pożyczka z bitu znaku),
    • znaczniki sterujące:
      • TF: znacznik pracy krokowej (stan równy 1 powoduje wprowadzenie procesora w tryb pracy umożliwiający wygenerowanie przerwania i przejście do specjalnych procedur obsługi po każdym wykonanym rozkazie),
      • IF: znacznik zezwolenia na przerwanie (ustawiony w stan 1 powoduje odblokowanie systemu przerwań procesora),
      • DF: znacznik kierunku (jeżeli ma wartość 0 to przetwarzanie łańcuchów odbywa się z inkrementacją adresów).

Rejestry AX, BX, CX, DX, SI, DI, BP oraz SP w czasie wykonania programu mogą zawierać odpowiednio:
  • argumenty dla wykonywanych w programie operacji arytmetycznych i logicznych,
  • argumenty służące do obliczania adresu w pamięci operacyjnej,
  • wskaźniki do pamięci operacyjnej.

W procesorze 80386 w miejsce wymienionych wyżej rejestrów 16-bitowych wprowadzono rejestry 32-bitowe (i tak pozostało do procesorów o architekturze x86-32 pracujących w trybie 32-bitowym):
  • EAX (32 bity),
  • EBX (32 bity),
  • ECX (32 bity),
  • EDX(32 bity),
  • ESI (32 bity),
  • EDI (32 bity ,
  • EBP (32 bity),
  • ESP (32 bity).

Począwszy od procesora 80386 mamy też do dyspozycji 32-bitowy rejestr wskaźników rozkazów EIP, a także 32-bitowy rejestr znaczników EFLAGS. W rejestrze EFLAGS oprócz znanych z procesora 8086 znaczników stanu oraz znaczników sterujących mamy również znaczniki systemowe.

Równolegle do procesora 8086 powstał koprocesor arytmetyczny 8087, a później dla kolejnych procesorów 80286 i 80386 koprocesory 80287 i 80387. Koprocesor arytmetyczny posiada 80-bitowe rejestry oraz listę instrukcji pozwalającą na stosunkowo proste wykonywanie nawet bardzo złożonych operacji matematycznych na liczbach zmiennoprzecinkowych. Począwszy od procesora i486 (80486) koprocesor arytmetyczny włączony został do procesora głównego przy zachowaniu wszystkich swoich funkcji. W procesorach o architekturze x86-32 występuje w postaci tzw. jednostki zmiennoprzecinkowej. Liczby zmiennoprzecinkowe są tam składowane w rejestrach od R0 do R7. Programista nie ma bezpośredniego dostępu do tych rejestrów lecz może się do nich odwoływać poprzez aliasy jakimi są rejestry od ST(0) do ST(7).

Kolejne rejestry zostały udostępnione w procesorze Pentium MMX, który był przejściowym modelem i dlatego mówi się raczej, że procesorze Pentium 2 zostało wprowadzone rozszerzenie MMX. Rozszerzenie to wprowadza osiem 64-bitowych rejestrów oznaczonych od MM0 do MM7.

W procesorze Pentium 3 wprowadzono tym razem osiem rejestrów o rozmiarze 128 bitów, które stanowią podstawowe zasoby rozszerzenia procesora o architekturze x86-32 o nazwie SSE. W procesorze Pentium 4 nie przybyło już żadnych dostępnych programowo rejestrów, a została jedynie rozbudowana lista instrukcji. Rozszerzenie procesora Pentium 4 nazwano zatem SSE2. Rejestry rozszerzeń SSE i SSE2 nazywamy XMM (oznaczane od XMM0 do XMM7). Później Intel wprowadził na rynek wersję procesora Pentium 4 z jądrem o nazwie Prescott, w którym wprowadzono rozszerzenie SSE3. W następnych wersjach procesorów pojawiły się jeszcze rozszerzenia SSE3 i SSE4.

Dane przetwarzane w programie mogą znajdować się w opisanych wyżej rejestrach tudzież w pamięci operacyjnej. Pamięć operacyjna ma organizację bajtową czyli każdy bajt w pamięci ma swój własny fizyczny adres. Podzielona jest ona na części zwane segmentami. Maksymalna wielkość pamięci operacyjnej zależy od wielkości magistrali adresowej. Każdy bajt w pamięci adresować będziemy przez adres logiczny (a nie fizyczny), który składa się z dwóch części:
  • adresu początku segmentu,
  • adresu względem początku segmentu (offsetu).

Do określenia offsetu służą rejestry segmentowe. Procesor 8086 ma ich cztery, a procesor 80386 i kolejne ma ich sześć:
  • CS (16 bitów) wskazuje segment z programem,
  • DS (16 bitów) wskazuje segment z danymi,
  • SS (16 bitów) wskazuje segment stosu,
  • ES (16 bitów) wskazuje dodatkowe segmenty danych,
  • FS (16 bitów) wskazuje dodatkowe segmenty danych,
  • ES (16 bitów) wskazuje dodatkowe segmenty danych.

Jeżeli chodzi o tryb 64-bitowy procesorów to programista otrzymuje:
  • rejestry ogólnego przeznaczenia, na których mapowane są 32-bitowe rejestry ogólnego przeznaczenia:
    • RAX (64 bity),
    • RBX (64 bity),
    • RCX (64 bity),
    • RDX (64 bity),
    • RSI (64 bity),
    • RDI (64 bity),
    • RBP (64 bity),
    • RSP (64 bity),
  • nowe rejestry ogólnego przeznaczenia:
    • R8 (64 bity),
    • R9 (64 bity) ,
    • R10 (64 bity),
    • R11 (64 bity),
    • R12 (64 bity),
    • R13 (64 bity),
    • R14 (64 bity),
    • R15 (64 bity),
  • nowe rejestry ogólnego przeznaczenia mapowane na rejestrach od R8 do R15:
    • R8D (32 bity),
    • R9D (32 bity) ,
    • R10D (32 bity),
    • R11D (32 bity),
    • R12D (32 bity),
    • R13D (32 bity),
    • R14D (32 bity),
    • R15D (32 bity),
  • nowe rejestry ogólnego przeznaczenia mapowane na rejestrach od R8D do R15D:
    • R8W (16 bitów),
    • R9W (16 bitów),
    • R10W (16 bitów),
    • R11W (16 bitów),
    • R12W (16 bitów),
    • R13W (16 bitów)
    • R14W (16 bitów),
    • R15W (16 bitów),
  • nowe rejestry ogólnego przeznaczenia mapowane na rejestrach od R8W do R15W:
    • R8L (8 bitów),
    • R9L (8 bitów),
    • R10L (8 bitów),
    • R11L (8 bitów),
    • R12L (8 bitów),
    • R13L (8 bitów),
    • R14L (8 bitów),
    • R15L (8 bitów),
  • nowe rejestry XMM:
    • XMM8 (128 bitów),
    • XMM9 (128 bitów),
    • XMM10 (128 bitów),
    • XMM11 (128 bitów),
    • XMM12 (128 bitów),
    • XMM13 (128 bitów),
    • XMM14 (128 bitów),
    • XMM15 (128 bitów).

Źródło: Wróbel E., Praktyczny kurs asemblera. Wydanie II, Helion SA, 2011