Procesor Intel 8086 — model programisty

Transkrypt

Procesor Intel 8086 — model programisty
Procesor Intel 8086 — model programisty
Arkadiusz Chrobot
26 września 2011
Spis treści
1 Wstęp
2
2 Rejestry procesora 8086
2
3 Adresowanie pamięci
4
4 Ważne elementy języka Pascal
8
1
1
Wstęp
Głównym celem zadań, które będą realizowane przez Państwa w ramach
laboratorium do przedmiotu „Systemy Operacyjne”, 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, 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
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
przesunięcie względem początku segmentu stosu. Rejestry AX, BX, CX i DX
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 wymieAX
AL
AH
BX
BH
BL
CX
CH
CL
DX
DL
DH
Rysunek 1: Rejestry 8-bitowe
nionych 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:
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
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
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
3
Nie jest to dokładne wytłumaczenie. Bardziej precyzyjny opis znajduje się w następnym rozdziale.
4
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
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
nazywać odpowiednio: częścią segmentową4 i offsetem lub inaczej przesunię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
4
Dosyć często tę część adresu nazywa się „segmentem”, ale jak wkrótce się przekonamy,
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.
5
6
16 B
16 B
16 B
.
.
.
.
1 MB
16 B
16 B
16 B
?
Rysunek 3: Pamięć podzielona na segmenty fizyczne.
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ę
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.
6
6
64 KB
6
16 B
?
64 KB
1 MB
.
.
.
.
?
Rysunek 4: Podział pamięci fizycznej na logiczne segmenty.
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 .
9
Z podanych trzech adresów, adresem kanonicznym jest tylko pierwszy.
7
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 ;
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 ;
8
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
9