1.7 Adresowanie pamięci

W każdym trybie procesora stosowany jest pewien sposób adresowania pamięci. Opiszę teraz jak to wygląda w trybie rzeczywistym. Jak wiadomo powszechnie dość dawno używało się tego trybu więc nie ma się co dziwić, że maksymalny rozmiar pamięci jaki można w nim zaadresować to trochę ponad 1MB. We wczesnych procesorach istniało tylko 20 linii adresowych co pozwalało na ustawienie jedynie 20 bitowego adresu. Twórcy procesora musieli jednak do dyspozycji jedynie 16 bitowe rejestry. Mamy dzięki temu adresowanie, które można nazwać segment:offset. Adres pamięci składany jest z dwóch części znajdujących się w dwóch rejestrach 16 bitowych. Co zapisuje się najczęściej w postaci dwóch liczb przedzielonych dwókropkiem. Pierwsza część adresu nazywana jest segmentem. Zgodnie z nazewnictwem umieszcza się ją zawsze w rejestrach segmentowych. Druga część składająca się na adres nazywana jest offsetem lub bardziej po polsku przesunięciem i umieszczana jest zwykle w rejestrach wskaźnikowych. Jednak pojemność obu 16 bitowych rejestrów razem daje 32 bity, a szyna adresowa jest 20 bitowa więc projektanci procesora wymyślili dość nietypowy sposób rozwiązania tego problemu. Poniżej podaję formułę przeliczania adresu segment:offset na adres pamięci fizycznej. Adres fizyczny jest niczym innym jak adresem danej komórki pamięci, który jest ustawiany bezpośrednio na szynie adresowej.

adres_fizyczny = ( segment * 0x10 ) + offset

(zapis 0x10 jest inną formą zapisu liczb w systemie hexadecymalnym, 0x10 = 10h)

Co można przedstawić bardziej obrazowo:

Oznaczę wartość segmentu jako XXXXh a offset jako YYYYh.

segment = XXXXh

segment * 0x10 = XXXX0h

segment * 0x10   XXXX0h
+         offset +  YYYYh
------------------ ---------------------
( segment * 0x10 ) + offset   ZZZZYh = adres fizycznej pamięci

W ten sposób otrzymujemy liczbę składającą się z 20 bitów czyli zgodną z szerokością szyny adresowej.

Ten typ przeliczania adresów na adresy fizyczne dzieli pamięć na tak zwane segmenty. Numer segmentu jest zapisywany w części segmentowej, a przesunięcie względem początku tego segmentu w części offsetowej. Łatwo więc przeliczyć, że pamięć dostępna w obrębie jednego segmentu ma rozmiar równy pojemności rejestru 16 bitowego co daje 65536 bajtów pamięci na jeden segment.

Jednak przeliczanie adresów segment:offset na adres fizyczny jest nie jednoznaczne. Co oznacza, że dwa różne adresy segment:offset mogą adresować tą samą komórkę pamięci fizycznej. Przedstawię to na przykładzie.

Mamy dwa adresy A = B800h:0112h, B = B011h:8002h. Przeliczamy je na adresy fizyczne, żeby móc zobaczyć do jakiej pamięci się odwołują.

B8000h   B0110h
+         0112h +  8002h
------------------ ---------------------
B8112h   B8112h

Jak widać te dwa różne adresy wskazują na ten sam bajt pamięci. Jest to pewien problem dla początkujących programistów ponieważ wprowadza pewną dezorientację i nie wiadomo gdzie tak naprawdę znajduje się nasz program lub dane. W tak adresowanej pamięci znajduje się maksymalnie 16 niezależnych segmentów o rozmiarze 65536 bajtów.

Łatwo można obliczyć o ile bajtów różnią się początki dwóch kolejnych segmentów w pamięci fizycznej. Segment 0001h zaczyna się w pamięci fizycznej od adresu 10h, a następny (0002h) zaczyna się od adresu 20h. Tak więc różnica pomiędzy początkami segmentów jest równa 16 bajtów, a ich rozmiary to 65536 B, w wyniku czego segmenty zachodzą na siebie. Segmentowanie można przedstawić w formie kartek papieru. Przypuśćmy, że mamy kilka kartek i każda kartka ma powierzchnię reprezentującą wielkość segmentu pamięci i ma szerokość 16 bajtów. Jeżeli ułożymy kartki jedna na drugiej z przesunięciem każdej następnej o linię 16 bajtową dostaniemy odwzorowanie tej pamięci w pamięć fizyczną. Co można sobie wyobrazić jako stos kartek ułożonych na sobie przesuniętych względem siebie o odległość 16 bajtów.

Widać, że przykładowy wyróżniony bajt można zaadresować trzema różnymi adresami w formie segment:offset.

Adresowanie pamięci w trybie chronionym bazuje na podobnej zasadzie jak w przedstawionym trybie rzeczywistym. Jednak z tą różnicą, że możemy sami dowolnie definiować rozmiar jak i położenie początku segmentu. Adresowanie pamięci za pomocą segmentów sprawiało jednak programistą dość dużo trudności więc w celu uproszczenia adresowania w trybie chronionym bardzo często całą pamięć opisuje się jednym segmentem o maksymalnym rozmiarze i początku w adresie 0. W 64 bitowym trybie chronionym posunięto się nawet dalej i praktycznie usunięto możliwość stosowania segmentowania pamięci. Więcej szczegółów odnośnie adresowania w trybie chronionym przedstawię w odpowiednim dziale.