Procesor Intel 8086

Transkrypt

Procesor Intel 8086
Procesor Intel 8086 — model programisty
dr inż. Arkadiusz Chrobot
28 września 2013
Spis treści
1 Wstęp
2
2 Rejestry procesora 8086
2
3 Adresowanie pamięci
4
4 Ważne elementy języka Pascal
7
1
1
Wstęp
Głównym celem zadań, które będą realizowane przez Państwa w ramach
laboratorium do przedmiotu „Systemy Operacyjne 1”, jest poznanie budowy
i działania jednego z najprostszych systemów operacyjnych: ms-dos. System
ten był pierwotnie przeznaczony dla komputerów wyposażonych w 16-bitowe
procesory firmy Intel. Współcześnie, procesor każdego komputera klasy pc
umożliwia pracę w tzw. trybie rzeczywistym i trybie wirtualnym, w których
jest wstecznie zgodny z tymi układami. Nawet pobieżna znajomość budowy
16-bitowych mikroprocesorów Intela znacznie ułatwi zadanie poznania funkcjonowania oprogramowania napisanego dla nich. Naszą uwagę skupimy na
opisie cech procesora 8086, które mają największe znaczenie dla programisty.
2
Rejestry procesora 8086
Formalnie, przedstawienie modelu programisty danego procesora powinno się zacząć od omówienia jego listy rozkazów. Niestety objętość tej instrukcji, ani czas przeznaczony na zajęcia laboratoryjne nie pozwalają na
to. Nasz opis zaczniemy więc od rejestrów, które są dostępne, bezpośrednio
lub pośrednio dla programisty.
Procesor 8086 wyposażony jest w osiem 16-bitowych rejestrów, które
zwykło się nazywać rejestrami ogólnego przeznaczenia. Są to rejestry ax,
bx, i cx, dx, si, di, sp i bp. Rzeczywiście, każdy z nich może zostać użyty
jako rejestr, w którym przechowywane są dane, jednakże niektóre z rozkazów
procesora 8086 traktują te rejestry w specjalny sposób. Rejestr ax nazywany jest rejestrem akumulatora, ponieważ z niego muszą korzystać operacje
arytmetyczne (konkretnie: mnożenie i dzielenie). Rejestr bx jest nazywany
rejestrem bazowym, ponieważ jest wykorzystywany w niektórych trybach
adresowania do przechowywania części adresu nazywanej przesunięciem lub
offsetem1 . Adres ten wskazuje komórkę w obszarze pamięci nazywanym segmentem danych2 . Rejestr cx jest nazywany rejestrem licznikowym, ponieważ korzysta z niego instrukcja pętli (ang. loop) przechowując liczbę powtórzeń, które musi jeszcze wykonać. Rejestr dx może pełnić rolę rozszerzenia
akumulatora w instrukcjach mnożenia i dzielenia (są w nim przechowywane starsze bity wyniku). Rejestry si i di pełnią rolę rejestrów indeksowych
w instrukcjach łańcuchowych. si zawiera offset źródła, a di offset przeznaczenia. Rejestr sp jest używany przez instrukcje obsługujące sprzętowy stos
i nazywany jest wskaźnikiem stosu (ang. stack pointer ). Ostatnim z grupy
omawianych rejestrów jest rejestr bp, który może pełnić podobną rolę jak
rejestr bx (dlatego również nazywany jest rejestrem bazowym), ale zawiera
przesunięcie względem początku segmentu stosu. Rejestry ax, bx, cx i dx
1
2
Termin ten będzie objaśniony w rozdziale o adresowaniu pamięci.
Ten termin również będzie opisany w rozdziale poświęconym adresowaniu pamięci.
2
odznaczają się pewną szczególną cechą. Każdy z nich jest złożony z dwóch
8-bitowych rejestrów. Ich nazwy są dwuliterowe. Nazwa rejestru 8-bitowego,
który zawiera osiem starszych lub inaczej górnych bitów rejestru 16-bitowego
kończy się literą h, a nazwa rejestru zawierającego osiem młodszych (dolnych) bitów kończy się na l. Pierwsza litera jest również pierwszą literą
nazwy rejestru, który tworzą, np.: ax ⇒ ah i al. Oprócz ośmiu wymienioax
ah
al
bx
bh
bl
cx
ch
cl
dx
dh
dl
Rysunek 1: Rejestry 8-bitowe
nych rejestrów ogólnego przeznaczenia istnieje 6 innych, które mają ściśle
określone role. Każdy z nich jest rejestrem 16-bitowym. Pierwszy z nich, to
rejestr ip, który jest nazywany wskaźnikiem rozkazów, bowiem przechowuje
offset adresu następnej instrukcji, jaką wykona procesor. Programista nie
może wpływać bezpośrednio na zawartość tego rejestru. Jedynym sposobem
zmiany jego zawartości jest wykonanie dowolnej instrukcji skoku. Kolejnym
rejestrem jest rejestr flag. Stany poszczególnych bitów tego rejestru określają stan w jakim znajduje się procesor. Szczegółowo objaśnia to rysunek nr
2. Umieszczone w nim skróty mają następujące znaczenie:
15
14
13
12
11
10
9
8
7
6
of df if tf sf zf
5
4
af
3
2
pf
1
0
cf
Rysunek 2: Rejestr flag
cf - znacznik przeniesienia (po wykonaniu operacji arytmetycznej nastąpiło przeniesienie jedynki na najstarszej pozycji jej wyniku),
pf - znacznik parzystości (wynik ostatniej operacji logicznej lub arytmetycznej zawiera parzystą liczbę jedynek),
3
af - znacznik dodatkowego przeniesienia (jak CF, ale przeniesienie było
na czwartym bicie),
zf - znacznik zera (wynik ostatniej operacji arytmetycznej lub logicznej
był zerem),
sf - znacznik znaku (określa, czy wynik ostatniej operacji arytmetycznej
był dodatni, czy ujemny,
tf - znacznik pułapki (określa, czy procesor pracuje w trybie debugowania),
if - znacznik przerwań (określa, czy włączone są przerwania),
df - znacznik kierunku (określa kierunek operacji łańcuchowych),
of - znacznik przepełnienia (wynik ostatniej operacji arytmetycznej nie
mieści się w akumulatorze),
Ostatnie cztery rejestry, to cs, ds, ss i es. Są one nazywane rejestrami segmentowymi. Procesor 8086, jak większość innych procesorów realizuje przetwarzanie oparte o model opracowany przez Johna von Neumanna. Oznacza
to, że rozkaz lub dana może zostać umieszczona w dowolnej komórce pamięci operacyjnej. Jednakże procesor musi wiedzieć, który obszar pamięci
zawiera instrukcje do wykonania, a który dane, na których te instrukcje
powinny zostać wykonane. Rejestry segmentowe zawierają właśnie adresy
początków tych obszarów3 . Rejestr cs zawiera adres początku obszaru zawierającego instrukcje do wykonania, ds — adres początku obszaru pamięci
przechowującego dane, ss — adres początku obszaru pamięci w którym jest
zorganizowany stos. es jest dodatkowym rejestrem segmentowym, który może wskazywać dowolny z wymienionych obszarów lub inny, np. pusty obszar.
3
Adresowanie pamięci
Z punktu widzenia programisty komputerowego, najważniejszą cechą,
jeśli chodzi o zestaw procesor-pamięć jest to, ile pamięci może procesor zaadresować. Okazuje się jednak, że procesor 8086 narzuca również pewien
szczególny sposób jej adresowania, o którym osoba pisząca program musi
wiedzieć.
Czynnikiem determinującym rozmiar pamięci, jaką może się posługiwać
procesor, jest szerokość jego magistrali adresowej, czyli połączenia między
nim a pamięcią. W przypadku procesora 8086 ta szyna jest 20-bitowa. Oznacza to, że omawiany procesor może zaadresować 220 = 1 MiB pamięci operacyjnej. Dosyć często zachodzi konieczność wykonania jakiejś operacji na
3
Nie jest to dokładne wytłumaczenie. Bardziej precyzyjny opis znajduje się w następnym rozdziale.
4
adresie, np. zwiększenia go o jeden. Aby móc to zrobić procesor musi gdzieś
ten adres zapamiętać. Najbardziej odpowiednim miejscem jest oczywiście
któryś z rejestrów, ale one wszystkie są co najwyżej 16-bitowe. Rozwiązanie problemu jest stosunkowo proste: procesor 8086 używa dwóch rejestrów
do zapamiętania adresu w pamięci. Starsze 16 bitów jest zapamiętywanych
w jednym z rejestrów segmentowych, a młodsze 4 w np. rejestrze bx lub
dx. Takie rozwiązanie dzieli pamięć fizyczną na obszary o wielkości 16 bajów każdy, tak jak to pokazuje rysunek 3. Te dwie części adresu będziemy
16 B
16 B
16 B
..
.
1 MiB
16 B
16 B
16 B
Rysunek 3: Pamięć podzielona na segmenty fizyczne.
nazywać odpowiednio: częścią segmentową4 i offsetem lub inaczej przesu4
Dosyć często tę część adresu nazywa się „segmentem”, ale jak wkrótce się przekonamy,
5
nięciem5 . Aby wskazać więc konkretną komórkę6 w pamięci, musimy podać
część segmentową i offset jej adresu. Obszar o wielkości 16 bajtów będziemy
nazywać segmentem fizycznym lub paragrafem. Zauważmy, że adres z liczby
20-bitowej stał się liczbą 32-bitową. Adres 20-bitowy będziemy nazywać adresem fizycznym, a 32-bitowy adresem logicznym. Przy takim rozszerzeniu
adresu powstaje jednak pewien problem. Otóż adres jest traktowany tak
samo, jak każda inna dana, co oznacza, że nie ma żadnego wbudowanego
w procesor mechanizmu, który wymusiłby na programiście przestrzeganie
podziału adresu na 16 i 4 bity. W praktyce oznacza to, że programista może
użyć jako adresu dowolnej liczby 32- bitowej. Taki adres jest oczywiście podzielony na dwie 16-bitowe części. O ile rozmiar części segmentowej nie uległ
zmianie, to rozmiar offsetu wzrósł o 12 bitów. Oznacza to, że segmenty generowane przez adres logiczny mają wielkość 216 = 65536 B (64 KiB). Należy
również zauważyć, że 32-bitowy adres pozwala zaadresować 232 = 4 GiB pamięci. W jaki więc sposób pogodzić ze sobą te dwie wydawałoby się sprzeczne
„wizje” pamięci? Rozwiązanie pokazuje rysunek 4. Segmenty logiczne „nachodzą” na siebie tak, że początek następnego segmentu znajduje się wewnątrz poprzedniego. Każdy kolejny segment logiczny rozpoczyna się 16
bajtów „dalej” od początku poprzedzającego go segmentu.
Pozostaje do opisania kwestia przeliczania adresu logicznego na fizycz7
ny . Otóż adres logiczny zapisywany jest najczęściej w postaci dwóch liczb
szesnastkowych rozdzielonych znakiem dwukropka, np: 7FFFh:023Ah8 . Żeby przeliczyć go na adres fizyczny wystarczy pomnożyć przez 16 wartość
części segmentowej i do wyniku dodać wartość offsetu. Nasz przykładowy
adres logiczny będzie więc przeliczony następująco: 7F F F h∗ 10h + 023Ah =
7F F F 0h + 023Ah = 8022Ah. Ze względu na opisany wyżej układ segmenty
logiczne mają pewne obszary wspólne, co dobrze ilustruje rysunek numer
4. Przekształcenie adresu logicznego w fizyczny nie może więc być jednoznaczne, tzn. niektóre adresy logiczne, które nawet „optycznie” wydają się
różne, dają po przekształceniu ten sam adres fizyczny, np.: 07C0h : 0001h =
07C01h, 0700h : 0C01h = 07C01h, 0780h : 0501h = 07C01h. Aby uniknąć
takiej sytuacji zaleca się stosowanie adresów kanonicznych, czyli takich w których część segmentowa jest dowolna, natomiast wykorzystywane są tylko
4 najmłodsze bity offsetu (pozostałe są zawsze równe zero)9 .
termin ten jest niejednoznaczny.
5
Pełny termin brzmi „przesunięcie względem początku segmentu”. Początek segmentu
wyznacza oczywiście część segmentowa adresu.
6
inaczej: konkretny bajt
7
Operacja odwrotna jest opisana wcześniej.
8
Gwoli przypomnienia - „h” oznacza, że mamy do czynienia z liczbą szesnastkową. Jeśli
część segmentowa adresu zapisana jest np. w rejestrze ds, a offset w rejestrze dx, to adres
można zapisać w postaci ds:dx.
9
Z podanych trzech adresów, adresem kanonicznym jest tylko pierwszy.
6
64 KiB
16 B
64 KiB
1 MiB
..
.
Rysunek 4: Podział pamięci fizycznej na logiczne segmenty.
4
Ważne elementy języka Pascal
Ten rozdział nie jest poświęcony procesorowi 8086, ale ma za zadanie
przedstawienie elementów języka (Turbo) Pascal, które pozwalają na współpracę z systemem ms-dos i mogą być przydatne podczas realizacji zajęć.
Więcej informacji na temat tych elementów można uzyskać w pomocy środowiska Turbo Pascal, lub z książek wymienionych w bibliografii.
1. absolute — słowo kluczowe, które pozwala umieścić zadeklarowaną
zmienną pod określonym adresem w pamięci, np.:
var
i v : array [ 0 . . 2 5 5 ] of p o i n t e r a b s o l u t e $0000 : $0000 ;
7
2. mem,memw,meml — tablice skojarzone z pamięcią komputera, które
pozwalają odczytać odpowiednio: bajt, słowo i podwójne słowo z określonego adresu pamięci, np.:
var
x : byte ;
begin
x:=mem[ $0000 : $0000 ] ;
end .
3. ptr — funkcja pozwalająca utworzyć wskaźnik do miejsca w pamięci
komputera na podstawie części segmentowej i offsetu, np.:
var
x : pointer ;
begin
x:= p t r ( $0000 , $0000 ) ;
end .
4. seg, ofs — funkcje, które zwracają część segmentową i offset adresu
zmiennej, przekazanej im jako parametr wywołania,
var
x : byte ;
s , o : word ;
begin
s := s e g ( x ) ;
o:= o f s ( x ) ;
end .
5. port,portw — tablice, które pozwalają odczytywać zawartość wybranych portów komputera.
var
x : byte ;
begin
x:= p o r t [ $60 ] ;
end .
Literatura
[1] Gary Syck, Turbo Assembler - Biblia użytkownika, LP&T, Warszawa
1994
[2] Leonid Bułhak, Ryszard Goczyński, Michał Tuszyński, DOS 5 od środka,
HELP, Warszawa 1997
8