Architektura Komputerów/Wykład 4: Struktura modelu programowego

From Studia Informatyczne


Grafika:ASK_M4_S01.png

Grafika:ASK_M4_S02.png

Grafika:ASK_M4_S03.png

Użytkowy model programowy – to zbiór zasobów logicznych komputera widzianych przez programistę lub kompilator. Model programowy nie ma jednoznacznego i bezpośredniego związku z implementacją, czyli budową procesora i komputera. Istnieje wiele rodzin komputerów czy procesorów o wspólnym modelu programowym i wielu odmiennych implementacjach. Przykładem może to być rodzina x86 (zapoczątkowana w 1976 roku) lub IBM zSeries, wywodząca się z lat 60-tych XX wieku.


Grafika:ASK_M4_S04.png

Grafika:ASK_M4_S05.png

Grafika:ASK_M4_S06.png

Rejestry procesora mogą pełnić różne role w programach.

Akumulatorem nazywamy rejestr, który może być użyty jako argument źródła i równocześnie przeznaczenia dla operacji arytmetycznej lub logicznej.

Rejestr służący do uzyskania adresu danej umieszczonej w pamięci nazywa się ogólnie rejestrem adresowym. Jeśli rejestr może być użyty w trybie adresowania rejestrowym pośrednim, jest on nazywany rejestrem bazowym.

Rejestr może również służyć do odliczania iteracji pętli. Rejestr przewidziany do takiego zastosowania nazywa się licznikiem pętli.


Grafika:ASK_M4_S07.png

istnieje wiele różnych koncepcji organizacji zestawu rejestrów – począwszy od architektur bezrejestrowych a skończywszy na architekturach z bardzo dużym zestawem rejestrów o liczebności przekraczającej 100.


Grafika:ASK_M4_S08.png

Architektury bezrejestrowe, określane również mianem „pamięć-pamięć”, nie przechowują danych w rejestrach procesora. Procesor jest wyposażony w kilka rejestrów, które służą wyłącznie do przechowywania adresów.

Szczególną postacią architektury bezrejestrowej jest architektura MOVE. W architekturach tego typu procesor wykonuje tylko jedną lub co najwyżej kilka instrukcji, których argumentami mogą być wyłącznie stałe lub adresy pamięci. operacje arytmetyczne i logiczne są realizowane poprzez przesłania do specjalnych lokacji pamięci, służących jako rejestry argumentów. Adresy tych lokacji są używane do wyboru operacji wykonywanej przez jednostkę arytmetyczną.


Grafika:ASK_M4_S09.png

Architektury z minimalnym zestawem rejestrów posiadają jeden lub dwa akumulatory i jeden lub dwa rejestry adresowe. niemal wszystkie instrukcje wymagają odwołania do pamięci, ale jeden z argumentów instrukcji zwykle znajduje się w rejestrze. Generowanie kodu dla takich procesorów jest proste, ale uzyskany program wykonuje się stosunkowo wolno wskutek dużej liczby odwołań do pamięci.

Architektury tego typu nie są stosowane w komputerach uniwersalnych, były one natomiast popularne w mikrokontrolerach.


Grafika:ASK_M4_S10.png

Rysunek przedstawia zestaw rejestrów jednostki wykonawczej mikrokontrolera rodziny HC08. Argumenty i zmienne lokalne procedur są tu adresowana względem wskaźnika stosu, a wielofunkcyjny rejestr HX służy jako rozszerzenie akumulatora dla operacji mnożenia i dzielenia, rejestr adresowy i licznik pętli. Podstawowym akumulatorem jest rejestr A.

Mały zestaw rejestrów wymusza umieszczanie niektórych zmiennych roboczych (tymczasowych) w pamięci.


Grafika:ASK_M4_S11.png

W architekturach z małym zestawem rejestrów specjalizowanych mamy do czynienia z zestawem kilku rejestrów, niekiedy częściowo uniwersalnych, które są argumentami domyślnymi wielu specyficznych instrukcji.

Ponieważ kompilator używa czasem instrukcji, których argumenty muszą być umieszczone w konkretnych rejestrach, rejestry nie mogą być użyte do przechowywania danych programu (argumentów i zmiennych lokalnych procedur). Służą one jedynie do przechowywania tymczasowych wyników obliczeń oraz jako argumenty instrukcji, z którymi są domyślnie związane.

Pomimo, że pojemność zestawu rejestrów jest znacząca, kompilator nie jest w stanie efektywnie korzystać z rejestrów.


Grafika:ASK_M4_S12.png

Rysunek przedstawia zestaw podstawowych rejestrów jednostki stałopozycyjnej 16-bitowej wersji architektury x86, zapoczątkowanej modelem Intel 8086 w 1976 roku. Poszczególne 16-bitowe rejestry są „przywiązane” do niektórych instrukcji. Tylko cztery rejestry mogą być używane jako adresowe.

Stosunkowo najbardziej uniwersalnymi rejestrami są SI i DI, ale ich użycie jest ograniczone przez fakt, że nie jest dostępny ich najmniej znaczący bajt. Niektóre bardziej zaawansowane kompilatory dla x86 używały tych dwóch rejestrów do alokacji zmiennych lokalnych procedury.


Grafika:ASK_M4_S13.png

W architekturach z małym zestawem rejestrów uniwersalnych mamy do czynienia z zestawem około 8 rejestrów, z których przynajmniej 5 daje się użyć w dowolnym charakterze – jako akumulatory lub rejestry adresowe.

Przykładem jest tu 32-bitowa wersja x86, zapoczątkowana przez model 80386 w roku 1985. Rejestry odziedziczone po wersji 16-bitowej zostały rozszerzone do 32 bitów. Wprowadzono również nowy zestaw trybów adresowania, umożliwiający wykorzystanie do adresowania wszystkich rejestrów. Wprowadzenie nowych instrukcji mnożenia umożliwiło zmniejszenie „przywiązania rejestrów do instrukcji. W ten sposób, pomimo, że na pierwszy rzut oka model programowy wygląda bardzo podobnie do wcześniejszych wersji, kompilator może używać rejestrów znacznie elastyczniej niż we wcześniejszych procesorach tej rodziny.


Grafika:ASK_M4_S14.png

W architekturach z dużym zestawem rejestrów mamy do czynienia z wieloma rejestrami, które mogł pełnić dowolną rolę. Umożliwia to kompilatorom używanie rejestrów do przekazywania argumentów wywołania i zmiennych lokalnych procedury.

Odwołania do pamięci zostają skupione w prologu i epilogu procedury, gdzie zachodzi przeładowanie ramki stosu pomiędzy rejestrami i stosem w pamięci.


Grafika:ASK_M4_S15.png

W niektórych architekturach bardzo duży zestaw rejestrów jest używany do zbuforowania wewnątrz procesora wierzchołka stosu umieszczonego w pamięci. W ten sposób uzyskuje się znaczną redukcję liczby odwołań do pamięci, również w prologu i epilogu procedury.

Ponieważ zestaw rejestrów mieści kilka ramek stosu, odwołania do pamięci zachodzą przy przepełnieniu lub niedopełnieniu stosu w rejestrach, co ma miejsce raz na kilka poziomów wywołań procedur.


Grafika:ASK_M4_S16.png

W architekturach ze stosowym zestawem rejestrów rejestry są zorganizowane w postaci niewielkiego stosu. Ponieważ operacje są domyślnie wykonywane na wierzchołku stosu, procesory o takich zestawach rejestrów mają zwykle instrukcje bezargumentowe lub jednoargumentowe.

Taka architektura zestawu rejestrów znacząco upraszcza konstrukcję kompilatora, jest ona jednak bardzo trudna do efektywnej realizacji przy typowych współczesnych strukturach jednostek wykonawczych.


Grafika:ASK_M4_S17.png

Drugim elementem modelu programowego jest zestaw trybów adresowania. Samo pojęcie trybu adresowania ma dwa znaczenia.


Grafika:ASK_M4_S18.png

Grafika:ASK_M4_S19.png

Grafika:ASK_M4_S20.png

Można zauważyć, że w celu umożliwienia efektywnej implementacji języków wysokiego poziomu procesor powinien posiadać trzy tryby adresowania.

Tryb natychmiastowy służy do ładowania do rejestrów stałych, w tym również adresów danych statycznych. Tryb rejestrowy bezpośredni umożliwia użycie zawartości rejestru jako argumentu operacji. Do adresowania danych w pamięci wystarczy jeden z trybów rejestrowych pośrednich. W ten sposób uzyskujemy możliwość adresowania zmiennym adresem, wyliczonym uprzednio w rejestrze. Adresowanie danych stałym adresem może być potraktowane jako szczególny przypadek adresowania ze zmiennym adresem. Najwygodniejszym trybem adresowania pamięci jest tryb rejestrowy pośredni z przemieszczeniem.


Grafika:ASK_M4_S21.png

Grafika:ASK_M4_S22.png

Tryby rejestrowe pośrednie z bazą w liczniku instrukcji umożliwiają adresowanie danych względem adresu bieżącej instrukcji. Mogą one być używane do adresowania danych wplecionych w kod i logicznie należących do kodu programu (np. tablice adresów). W procesorach 64-bitowych tryby te umożliwiają ograniczenie długości adresów absolutnych zawartych w zapisie instrukcji, gdyż dane, przynajmniej statyczne, są zwykle położone niezbyt daleko od kodu programu. (W przeciwnym razie zamiast 32-bitowych przemieszczeń względem PC do adresowania danych należałoby używać 64-bitowych adresów absolutnych.)

Problem obliczania zmieniających się z każdą instrukcją przemieszczeń danych liczonych względem PC przejmuje na siebie asembler.


Grafika:ASK_M4_S23.png

Grafika:ASK_M4_S24.png

Grafika:ASK_M4_S25.png

W trybach pamięciowych pośrednich jeden z elementów adresu jest pobierany z pamięci, a uzyskany adres jest wykorzystywany do odwołania do danej w pamięci. Odwołanie do danej wymaga więc dwukrotnego sięgnięcia do pamięci.

W trybach pamięciowych pośrednich występują dwa adresy efektywne, do obliczenia których można korzystać z operacji indeksowania lub dodawania przemieszczenia. Pierwszy adres, wyznaczony z jednego z omówionych wcześniej trybów adresowania pamięci, jest używany do odczytania wartości, służącej jako adres bazowy dla drugiego adresu. Do pozyskanego w ten sposób adresu bazowego można następnie dodać przemieszczenie lub przeskalowaną zawartość rejestru indeksowego.

Tryby pamięciowe pośrednie były charakterystyczne dla złożonych architektur CISC. Obecnie są one rzadko spotykane, gdyż opisana sekwencja operacji może być łatwo, a zarazem w sposób bardziej elastyczny, zrealizowana na drodze programowej.


Grafika:ASK_M4_S26.png

Model operacji warunkowych jest trzecim składnikiem modelu programowego. Współczesne procesory implementują jeden z trzech modeli opisanych w dalszym ciągu wykładu.


Grafika:ASK_M4_S27.png

Model ze znacznikami wprowadza dwufazową realizację operacji warunkowych, przy czym instrukcje procesora realizujące obie fazy mogą być rozsunięte w czasie.

W pierwszej fazie w wyniku wykonania operacji arytmetycznej lub logicznej zostają ustawione znaczniki – jednobitowe rejestry atrybutów wyniku. W drugiej fazie następuje wykonanie operacji zależne od stanu znaczników.


Grafika:ASK_M4_S28.png

W poszczególnych architekturach występuje od dwóch do sześciu znaczników. typowa liczba znaczników wynosi 4.Dwa ostatnie – przeniesienie pomocnicze i parzystość, występują głównie w architekturach wywodzących się z lat 70-tych XX wieku.

Znacznik przeniesienia pomocniczego wspomaga realizację operacji na liczbach zapisanych w kodzie BCD. Przechowuje on wartość przeniesienia pomiędzy tetradami pojedynczego bajtu reprezentującymi cyfry dziesiętne.

Znacznik parzystości był używany do podstawowej kontroli poprawności transmisji danych znakowych w kodzie ASCII, w tym do kontroli danych odczytywanych z taśm perforowanych. Nawet w procesorach 32- i 64-bitowych znacznik parzystości zawsze dotyczy wyłącznie najmniej znaczącego bajtu wyniku operacji.


Grafika:ASK_M4_S29.png

Ponieważ w różnych architekturach obowiązują różne konwencje ustawiania znaczników, rozpoczynając programowanie procesora w asemblerze należy dokładnie zapoznać się z dokumentacją poszczególnych instrukcji.


Grafika:ASK_M4_S30.png

Instrukcja warunkowa realizuje drugą fazę operacji warunkowej w modelu ze znacznikami. Jest to instrukcja, której sposób wykonania zależy od wartości znaczników. Instrukcja warunkowa specyfikuje warunek wykonania. Jeśli warunek (wyrażenie logiczne na znacznikach) jest spełniony, instrukcja wykonuje zadaną operację (np. skok lub przesłanie). W przeciwnym razie instrukcja wykonuje się jako pusta.

Instrukcja ustawienia danej (np. SETcc w x86) wpisuje do rejestru wartość logiczną warunku wyrażoną jako stałą całkowitą.

W niektórych architekturach większość instrukcji jest wykonywanych jako warunkowe.


Grafika:ASK_M4_S31.png

Warunek wykonania jest jednym z predefiniowanych w danej architekturze wyrażeń logicznych na znacznikach. Poszczególne warunki mają swoje nazwy mnemoniczne.


Grafika:ASK_M4_S32.png

Rysunek przedstawia rozmieszczenie znaczników w rejestrze stanu procesorów rodziny x86.


Grafika:ASK_M4_S33.png

Tabela zawiera nazwy symboliczne warunków dostępnych w x86 i specyfikację odpowiadających im wyrażeń logicznych na znacznikach.

Należy zwrócić uwagę na istnienie wielu nazw tych samych warunków, co ułatwia ich użycie w programach.

Stosowane w nazwach warunków skróty A, B, G i L (above, below, greater, less) oznaczają odpowiednio relacje większości i mniejszości dla liczb bez znaku (A, B) i ze znakiem (G, L).


Grafika:ASK_M4_S34.png

W modelu operacji warunkowych bez znaczników operacja warunkowa jest realizowana przez pojedynczą instrukcję. instrukcja ta ewaluuje wartość logiczną relacji, a następnie wykonuje jakąś czynność, jeśli relacja jest spełniona.

Taki model operacji warunkowych jest prosty i wydajny w implementacji. jest on stosowany w prostych architekturach RISCowych.


Grafika:ASK_M4_S35.png

Model z predykatami jest spotykany w nowych architekturach o dużej równoległości wykonania instrukcji, dużych zestawach rejestrów i wysokich kosztach skoków. praktycznie jedyną architekturą komercyjną implementującą ten model jest IA-64.