PRACA DYPLOMOWA:
PAMIĘĆ OPERACYJNA KOMPUTERÓW PC
autor:
Łukasz Sterkowicz
rok 1998
Bibliografia
Intel 80386 Programmer's Reference Manual. Intel Corporation, 1986.
eXtended Memory Specification (XMS), ver 3.0. Microsoft Corporation, Lotus Development Corporation, Intel Corporation, AST Research, Inc., 1988.
Virtual Control Program Interface (VCPI) specification, ver 1.0. Phar Lap Software, Inc., Quarterdeck Office Systems, 1989.
Dos Protected Mode Interface (DPMI) specification, ver 0.9. 1990.
Mapa pamięci IBM/PC. Arkadiusz Andrusz, Maciej Sokołowski. Lynx-SFT, Warszawa, 1995.
Encyklopedia informatyki. Stanisław Kruk. Pracownia Komputerowa Jacka Skalmierskiego, Gliwice, 1996.
Anatomia PC. Piotr Metzger. Helion, 1996.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
li {font-family: Times New Roman}
Spis treści
1. Wstęp ........................................................................................................ 5
1.1 Przyjęta konwencja ................................................................................... 6
2. Okiem technika ......................................................................................................... 7
2.1 Zasada działania pamięci .......................................................................... 8
2.2 Typy pamięci ............................................................................................ 9
2.3 Moduły pamięci ........................................................................................ 10
2.4 Układ DMA .............................................................................................. 11
3. Procesor a pamięć .................................................................................... 14
3.1 Pojęcie adresu ........................................................................................... 15
3.2 Rejestry procesora ..................................................................................... 15
3.3 Stronicowanie pamięci .............................................................................. 19
3.3.1 Pamięć wirtualna ....................................................................... 21
3.3.2 TLB ............................................................................................ 21
3.4 Tryb rzeczywisty ....................................................................................... 21
3.5 Tryb chroniony .......................................................................................... 23
3.5.1 Organizacja pamięci .................................................................. 23
3.5.2 Ochrona pamięci ........................................................................ 25
3.5.3 Wielozadaniowość ..................................................................... 27
3.5.4 Przerwania i wyjątki .................................................................. 29
3.6 Tryb V86 ................................................................................................... 31
4. Pamięć bazowa ......................................................................................... 32
4.1 Pamięć konwencjonalna ............................................................................ 33
4.1.1 Tablica wektorów przerwań ....................................................... 34
4.1.2 Zmienne BIOSu ......................................................................... 37
4.2 Pamięć górna ............................................................................................. 39
4.2.1 Pamięć wideo ............................................................................. 40
4.2.2 Obszary BIOSu .......................................................................... 41
4.2.3 Shadow RAM ............................................................................ 41
4.2.4 Bloki pamięci górnej .................................................................. 41
4.3 Struktury DOSu ........................................................................................ 42
4.3.1 Lista list ..................................................................................... 42
4.3.2 Bloki kontroli pamięci ............................................................... 43
4.3.3 Blok danych systemowych ........................................................ 44
4.3.4 Blok wstępny programu ............................................................. 44
4.4 Struktury DOSu ........................................................................................ 45
5. Pamięć rozszerzona ................................................................................. 46
5.1 Linia adresowa A20 .................................................................................. 47
5.2 Pamięć wysoka ......................................................................................... 50
5.3 Pamięć EMS ............................................................................................. 50
5.4 Schematy alokacji pamięci rozszerzonej .................................................. 59
5.4.1 BOTTOM-UP ............................................................................ 60
5.4.2 TOP-DOWN .............................................................................. 61
5.4.3 XMS .......................................................................................... 62
5.4.4 VCPI .......................................................................................... 67
5.4.5 DPMI ......................................................................................... 71
5.4.6 Alternatywny sposób alokacji .................................................... 85
5.5 DOS extendery ......................................................................................... 86
5.6 Funkcje BIOSu ......................................................................................... 87
5.7 Instalacja sterowników ............................................................................. 88
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
1. Wstęp
Niniejsze opracowanie zostało stworzone w taki sposób, aby docierało do szerokiej rzeszy odbiorców. Rozdział "Okiem technika" skierowany jest do osób chcących zapoznać się z pamięcią, o której wiedzą jedynie tyle że jest. Swoje trzy grosze znajdą też tam programiści wykorzystujący układ DMA. Kolejny rozdział "Procesor a pamięć" powinni przeczytać wszyscy chcący zagłębić się w dalsze części opracowania. Są tam zdefiniowane podstawowe pojęcia, i wiedza niezbędna do zrozumienia niektórych zagadnień. Temat poświęcony trybie chronionemu mogą pominąć początkujący programiści, gdyż jest on napisany z myślą o twórcach systemów i oprogramowania pracującego w tym trybie. Rozdział "Pamięć bazowa" poświęcony jest pamięci zarządzanej przez stary, lecz wciąż obecny system DOS. Ostatni z rozdziałów "Pamięć rozszerzona" dotyczy w dużej mierze sposobów udostępniania tej pamięci dla programów pracujących pod systemem DOS, lecz nie tylko. Skierowany jest on głównie do twórców DOS-extenderów, emulatorów DOS-u, lecz pożyteczne informacje znajdzie też zwykły użytkownik mający problemy ze skonfigurowaniem pamięci rozszerzonej.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
--></><basefont face="Times New Roman" size=3>
1.1. Przyjęta konwencja
Aby rozwiać wszelkie wątpliwości, poniżej przedstawiony jest sposób w jaki należy interpretować poszczególne elementy opracowania.
Nazewnictwo
Przyjęte nazewnictwo jest z godne z tym, które obowiązuje w ogólnodostępnej literaturze. Wyjątek dotyczy dwóch przypadków. Po pierwsze niektóre zwroty z języka angielskiego przetłumaczone zostały zgodnie z inwencją autora. Po drugie określenie "pamięć bazowa" jest w literaturze równoważne "pamięci konwencjonalnej", jednak autor postanowił je odnieść do obszaru pierwszego megabajta pamięci, gdyż nie ma funkcjonalnego określenia tego obszaru.
Funkcje API
W przedstawionych funkcjach API (Application Program Interface) przeważnie występują dwa przypadki zakończenia takiej funkcji. Chociaż nie jest to jawnie powiedziane, pierwszy z przypadków dotyczy poprawnego zakończenia, natomiast drugi błędnego.
Przykłady
Przedstawione przykłady w języku assemblera są zgodne ze składnią Turbo Assemblera (TASM) firmy Borland. Należy pamiętać iż nie są to w pełni działające programy, a jedynie ich fragmenty mające uzmysłowić działanie niektórych mechanizmów. Użyte dyrektywy ".CODE" i ".DATA" mają jedynie za zadanie wizualne oddzielenie fragmentów kodu od danych.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
--></><basefont face="Times New Roman" size=3>
2. Okiem technika
Nie sposób rozpocząć pracy poświęconej pamięci bez krótkiego rzutu okiem na nią od strony technicznej. Dlatego też treścią tego rozdziału jest budowa pamięci, typy w jakich ona występuje, oraz układy bezpośrednio z nią współpracujące.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
--></><basefont face="Times New Roman" size=3>
2.1. Zasada działania pamięci
Pamięć RAM fizycznie występuje w postaci układów scalonych (chipów). Każdy taki chip składa się z układów zwanych przerzutnikami, zazwyczaj są to przerzutniki bistabilne (flip-flop) typu D. Charakteryzują się one tym iż posiadają dwa stany równowagi trwałej, a więc mogą przechowywać jeden bit informacji (przyjmujący wartości 0 lub 1). Przerzutniki układane są w matrycę, dzięki czemu zapewniony jest swobodny dostęp do pamięci:
Specjalny układ zwany dekoderem adresu przekształca napływające adresy tak aby móc dokonać odczytu z matrycy. Sama matryca działa w ten sposób iż na żądaną linię słowa podajemy napięcie, po czym możemy pobrać/zapisać wartości na linii bitów.
Pamiętać należy iż architektura komputerów PC uniemożliwia nam programowy odczyt pojedynczego bitu. Najmniejszą jednostką którą możemy odczytać jest bajt (8 bitów).
Ze względu na różne elementy konstrukcyjne dzielimy pamięci na statyczną i dynamiczną.
Pamięć statyczna
Oto układ komórki statycznej przechowujący bit informacji:
Pamięć oparta o te układy charakteryzuje się tym iż jest nieulotna i nie wymaga odświeżania.
W zależności od tego jaką wartość reprezentuje komórka, jeden z tranzystorów przewodzi, a drugi jest w stanie odcięcia. Gdy komórka nie jest wybrana na linii słowa występuje napięcie 0,3 V dzięki czemu prąd płynący przez przewodzący tranzystor płynie do linii słowa (złącze połączone z linią bitów jest spolaryzowane zaporowo). Gdy chcemy odczytać stan, na linii słowa podajemy napięcie 3 V przez co spolaryzowane zaporowo staje się złącze z linią słowa, natomiast przewodzić zaczyna złącze z linią bitów. Spadek napięcia na odpowiednim rezystorze powoduje różnicę potencjałów która odczytywana jest przez wzmacniacz różnicowy jako 0 lub 1 (w zależności czy linia D czy D zaprzeczone ma wyższy potencjał). Podczas zapisu komórki na linii słowa również wystawiamy napięcie 3 V a na linie bitów żądane wartości, co spowoduje odpowiednie ustawienie tranzystorów.
Pamięć dynamiczna
Oto układ komórki dynamicznej przechowujący bit informacji:
Pamięć dynamiczna cechuje się ulotnością ze względu na użycie pojemności C. Wymaga to niestety odświeżania poprzez podanie odczytywanej wartości na sprzężenie zwrotne. Konieczny jest wtedy okresowy odczyt komórki, realizowany sprzętowo, lub programowo.
W przypadku wartości 1 kondensator jest załadowany, w przypadku 0 rozładowany. Zapisywanie wartości polega na podaniu stanu logicznego na linię bitów i napięcia na bramkę tranzystora T2 co powoduje ładowanie/rozładowanie kondensatora. Odczyt działa analogicznie, z tym że napięcie podajemy na bramkę T3. Wtedy prąd płynie przez tranzystory T1 i T3 do linii bitów.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
--></><basefont face="Times New Roman" size=3>
2.2. Typy pamięci
Pamięć operacyjna naszego komputera zwana jest pamięcią RAM (Random Access Memory), czyli pamięcią o dostępie swobodnym. Ze względu na różną budowę i dostęp, wśród pamięci RAM wyróżniamy poszczególne typy.
Podział ze względu na budowę
SRAM - (Static RAM), pamięć statyczna wykonana w oparciu o tranzystory bipolarne. Cechuje ją bardzo krótki czas dostępu do poszczególnej komórki i nieulotność. Niestety, pamięci SRAM są drogie, dlatego też wykorzystuje się je głównie jako pamięci cache.
DRAM - (Dynamic RAM) pamięć dynamiczna wykonana w oparciu o tranzystory MOS. Pamięć ta jest wolniejsza niż pamięć SRAM a w dodatku jest ona ulotna. Aby pamięć ta nie utraciła danych trzba ją odświeżać z częstotliwością co najmniej kilkaset Hz. Odświeżanie polega na zwykłym odczycie zawartości komórki.
SDRAM - (Synchronous Dynamic RAM) pamięć dynamiczna, synchroniczna. Pamięć ta jest podobna do pamięci DRAM, z tym że dostęp do komórek pamięci jest zsynchronizowany z zewnętrznym zegarem taktującym procesor.
Podział ze względu na dostęp
FPM RAM - (Fast Page Mode RAM), pamięć ta zorganizowana jest w strony, przy czym najszybciej realizowany jest dostęp do kolejnych komórek w obrębie strony.
EDO RAM - (Extended Data Output RAM), jest to pamięć w przypadku której w czasie odczytu danej komórki, może zostać pobrany adres następnej.
BEDO RAM - (Burst EDO RAM), w przypadku tej pamięci zamiast jednego adresu pobierane są cztery, przy czym na magistralę wystawiany jest tylko pierwszy co znacznie zwiększa szybkość dostępu.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
--></><basefont face="Times New Roman" size=3>
2.3. Moduły pamięci
Pamięć fizycznie występuje w postaci kości (układów scalonych), które mogą być całkowicie od siebie różne. Dlatego też powstały standardy konstrukcyjne, wymuszające łączenie kości w funkcjonalne moduły, które są zwyczajnymi płytkami drukowanymi z wlutowanymi chipami pamięci. Wraz z rozwojem komputerów i poszerzaniem szyny adresowej powstały różne typy modułów.
SIMM - (Single Inline Memory Module), moduł 32-stykowy, w którym szerokość szyny adresowej wynosi 8 bitów. Moduły te obecnie wykorzystywane są jedynie w niektórych kartach rozszerzających, gdyż płyty główne już dawno przestały je obsługiwać.
PS/2 - moduł 72-stykowy z 32-bitową szyną adresową. Jego nazwa powstała od rodziny komputerów PS/2, w których pierwotnie zainstalowano te moduły.
DIMM - (Dual Inline Memory Module) moduł 128-stykowy w którym szyna adresowa ma 64 bity. Jest to najnowszy standard konstrukcyjny wykorzystywanych w płytach z procesorem Pentium.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
--></><basefont face="Times New Roman" size=3>
2.4. Układ DMA
Układ DMA (Direct Memory Access) służy do bezpośredniej komunikacji z pamięcią bez użycia procesora. Najczęściej wymiana danych przebiega na linii pamięć - urządzenia zewnętrzne, lecz możliwa jest też wymiana pamięć - pamięć (obecnie bezużyteczna ze względu na powolność układów DMA oraz blokadę magistrali dla procesora).
Fizycznie DMA jest umieszczone w dwóch kościach 8237A, pełniących role układów master i slave, z których każdy obsługuje cztery kanały (slave: 0-3, master: 4-7). Kanał 4 jest używany do realizacji połączenia kaskadowego układu master z układem slave. Kanał 2 jest standardowo wykorzystywany przez kontroler stacji dyskietek. Należy dodać że kanały slave są 8-bitowe, natomiast kanały master w zależności od modelu komputera 16 lub 32-bitowe.
Wyróżniamy pięć trybów pracy każdego z kanałów DMA:
Tryb S - (Single), tryb w którym dokonywane jest pojedyncze przesłanie. Licznik transmisji jest zmniejszany a rejestr adresowy (w zależności od zaprogramowania) zmniejszany lub zwiększany. Kolejne przesłanie wymaga żądania przez urządzenie z którym następuje komunikacja (sygnał DREQ).
Tryb B - (Block), w tym trybie transmisja trwa nieprzerwanie do wystąpienia sygnału jej zakończenia (EOP), lub do osiągnięcia żądanej liczby przesłań.
Tryb D - (Demand), podobny do trybu B, z tym że transmisja może być chwilowo wstrzymana przez wystąpienie żądania o wyższym priorytecie, lub przez zanik sygnału na linii żądania (DREQ).
Tryb C - (Cascade), w rybie tym układ master jedynie przekazuje sygnały do układu slave. Dzięki niemu można zrealizować kaskadowe połączenie układów 8237A.
Tryb V - (Verify), tryb służący wewnętrznej weryfikacji układu. Układ wytwarza adresy i reaguje na sygnały, lecz nie generuje sygnałów dostępu do pamięci i urządzeń we/wy.
Programowanie układu DMA polega na zapisie odpowiednich portów. Najpierw maskujemy żądany kanał poprzez rejestr maski. Następnie wybieramy tryb za pomocą rejestru trybu oraz zapisujemy wartości rejestrów strony, adresu i licznika. Rejestry adresu są 16-bitowe (ustawiamy przerzutnik w stan początkowy, następnie zapisujemy młodszy bajt a potem starszy), natomiast rejestry stron 8-bitowe i uzupełniają one starszą część 24-bitowego adresu.
Poniższa tabela zawiera wszystkie adresy portów sterujących pracą układu DMA:
adres |
opis portu |
Porty układu slave |
|
000H |
Rejestr adresu kanału 0 |
001H |
Rejestr licznika kanału 0 |
002H |
Rejestr adresu kanału 1 |
003H |
Rejestr licznika kanału 1 |
004H |
Rejestr adresu kanału 2 |
005H |
Rejestr licznika kanału 2 |
006H |
Rejestr adresu kanału 3 |
007H |
Rejestr licznika kanału 3 |
008H |
Rejestr stanu (odczyt) |
008H |
Rejestr rozkazowy (zapis) |
009H |
Rejestr żądań |
00AH |
Rejestr maski kanału |
00BH |
Rejestr trybu |
00FH |
Rejestr maskujący |
081H |
Rejestr strony kanału 2 |
082H |
Rejestr strony kanału 3 |
083H |
Rejestr strony kanału 1 |
087H |
Rejestr strony kanału 0 |
Porty układu master |
|
089H |
Rejestr strony kanału 6 |
08AH |
Rejestr strony kanału 7 |
08BH |
Rejestr strony kanału 5 |
08DH |
Rejestr strony kanału 4 |
0C0H |
Rejestr adresu kanału 4 |
0C1H |
Rejestr licznika kanału 4 |
0C2H |
Rejestr adresu kanału 5 |
0C3H |
Rejestr licznika kanału 5 |
0C4H |
Rejestr adresu kanału 6 |
0C5H |
Rejestr licznika kanału 6 |
0C6H |
Rejestr adresu kanału 7 |
0C7H |
Rejestr licznika kanału 7 |
0D0H |
Rejestr stanu (odczyt) |
0D0H |
Rejestr rozkazowy (zapis) |
0D2H |
Rejestr żądań |
0D4H |
Rejestr maski kanału |
0D6H |
Rejestr trybu |
0DEH |
Rejestr maskujący |
Port 009H i 0D2H, Rejestr żądań
bity 3-7 - równe zero
bit 2 - jedynka oznacza uruchomienie transmisji
bity 0-1 - numer kanału, którego dotyczy żądanie
Port 008H i 0D0H, Rejestr stanu (tylko do odczytu)
bity 4-7 - jedynka oznacza wystąpienie zgłoszenia dla kanału
bity 0-3 - jedynka oznacza osiągnięcie liczby przesłań dla kanału
Port 008H i 0D0H, Rejestr rozkazowy (tylko do zapisu)
Rejestr ten służy do programowej kontroli pracy układu
Port 00AH i 0D4H, Rejestr maski kanału
bity 3-7 - równe zero
bit 2 - jedynka oznacza zamaskowanie kanału
bity 0-1 - numer kanału, którego dotyczy żądanie
Port 00FH i 0DEH, Rejestr maskujący
bity 4-7 - równe zero
bity 0-3 - jedynka oznacza zamaskowanie kanału
Port 00BH i 0D6H, Rejestr trybu
bity 6-7 - tryb pracy (00 - D, 01 - S, 10 - B, 11 - C)
bit 5 - 0 oznacza inkrementację adresu, 1 dekrementację
bit 4 - jedynka powoduje samooprogramowanie się układu
bity 2-3 - 00 - tryb V, 01 - zapis, 10 - odczyt
bity 0-1 - numer kanału, którego dotyczy żądanie
Dodatkowo występują także "sztuczne" porty, których specyfika polega na tym iż istotny jest tylko fakt zapisu do portu (argument przesyłany jest ignorowany). Porty te przedstawia tabela:
adres |
działanie portu |
Porty układu slave |
|
00CH |
Ustawienie przerzutnika w stan początkowy (używany przy przesyłaniu danych 16-bitowych, zapobiega pomyleniu młodszego bajtu ze starszym) |
00DH |
Programowa inicjalizacja układu |
00EH |
Wyzerowanie rejestru maski (uaktywnienie wszystkich kanałów) |
Porty układu master |
|
0D8H |
Ustawienie przerzutnika w stan początkowy |
0DAH |
Programowa inicjalizacja układu |
0DCH |
Wyzerowanie rejestru maski |
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
3. Procesor a pamięć
Pamięć jest niewątpliwie elementem architektury komputera, który najintensywniej współpracuje z procesorem. Do dyspozycji programisty istnieje wiele rejestrów i poleceń procesora, ułatwiających dostęp do pamięci, a także tryby pracy dla których zdefiniowane są różne jej modele. Dlatego mówiąc o pamięci nie można pominąć tak istotnego zagadnienia jak współpraca z procesorem. Niniejszy rozdział zawiera szczegółowy opis architektury procesora i jego mechanizmów. Znajduje się tutaj między innymi opis rejestrów i trybów pracy procesora 80386 i wyższych. Procesor ten, jako pierwszy 32-bitowy procesor Intela wprowadził standard dostępu do czterogigowej pamięci, jej ochrony i stronicowania, który praktycznie bez zmian zaimplementowany jest w najnowszych procesorach Pentium.
3.1. Pojęcie adresu
Adresem nazywamy pewną specyficzną liczbę, która opisuje komórkę pamięci. Za pomocą adresu informujemy procesor gdzie ma zostać zapisana, lub odczytana dana. Niestety, ze względu na różne sposoby dostępu procesora do pamięci samo pojęcie adresu niewiele już znaczy. Poniżej zdefiniowane są najistotniejsze pojęcia adresów, które należy poznać aby bez problemu orientować się podczas czytania specjalistycznej literatury:
adres fizyczny - (physical address) jest adresem najniższego poziomu. Podczas komunikacji procesora z układem obsługującym pamięć, na jego liniach adresowych wystawiany jest właśnie adres fizyczny. Wykorzystujemy go przy tworzeniu tablic implementujących stronicowanie, lub podczas komunikowania się z dowolnymi urządzeniami. Należy zauważyć, że podczas gdy mechanizm stronicowania pamięci jest wyłączony, adres fizyczny jest równy adresowi liniowemu,
adres liniowy - (linear address) jest formą przejściową między adresem fizycznym. Dzięki istnieniu adresu liniowego można uzyskać ciągłość pamięci nawet, jeżeli fizycznie jest ona porozrzucana. Adres liniowy jest zamieniany przez mechanizm stronicowania na adres fizyczny za pomocą tablic stron. W systemach wielozadaniowych niemożliwa jest wymiana adresów liniowych pomiędzy zadaniami, gdyż każde z nich może mieć oddzielne tablice stron. Adresy liniowe wykorzystujemy głównie przy tworzeniu deskryptorów,
adres logiczny - (logical address) jest adresem złożonym z dwóch członów: identyfikatora segmentu i przemieszczenia w tym segmencie. Dla trybu rzeczywistego i V86 identyfikatorem segmentu będzie jego adres liniowy podzielony przez 16, dla trybu chronionego będzie to selektor segmentu. Adres taki zapisujemy w postaci SEGMENT:OFFSET lub SELEKTOR:OFFSET. Adres logiczny powinien być wykorzystywany do zapisywania lub odczytywania danych, procesor automatycznie zamienia go na adres logiczny a następnie na adres fizyczny,
adres segmentowy - (segment) jest pierwszą częścią adresu logicznego. Jeżeli został podany bez przemieszczenia oznacza to, że wskazuje nie konkretną komórkę pamięci a cały blok. Domyślnie przyjmujemy przemieszczenie 0. Adres segmentowy zwracany jest przez niektóre funkcje interfejsów API (Application Program Interface),
adres relatywny - (offset) jest przemieszczeniem względem segmentu, lub jakiegoś bloku danych. Adres takiego segmentu musi być znany.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
3.2. Rejestry procesora
Pierwszym etapem poznawania architektury procesora są jego rejestry. W zależności od ich typów przechowują one dane, adresy pamięci lub flagi. Poniżej przedstawione są wszystkie rejestry procesorów 386 wzwyż, wraz ze szczegółowym opisem:
Rejestry podstawowe
Do tej grupy rejestrów należą 32-bitowe rejestry EAX, EBX, ECX, EDX, ESI, EDI, ESP i EBP. Każdy z nich ma możliwość odwołania się do mniej znaczącego słowa, a niektóre także do bajtów w tym słowie. W trybie rzeczywistym często znaczenie ma jedynie mniej znaczące słowo każdego z nich. Budowę rejestrów podstawowych przedstawia rysunek:
EAX - akumulator, jest używany w niektórych operacjach jak mnożenie, czy dzielenie,
EBX - rejestr bazowy, zawiera przemieszczenie względem segmentu danych,
ECX - rejestr licznikowy, określa liczbę wykonań pętli,
EDX - używany jako rozszerzenie akumulatora w operacjach 64-bitowych,
ESI - rejestr indeksowy, zawiera adres źródła w operacjach łańcuchowych,
EDI - rejestr indeksowy, zawiera adres przeznaczenia w operacjach łańcuchowych,
ESP - wskaźnik stosu,
EBP - rejestr bazowy, zawiera przemieszczenie względem segmentu stosu.
Rejestry segmentowe
W skład tej grupy wchodzą rejestry 16-bitowe CS, DS, SS, ES, FS i GS. Każdy z nich zawiera identyfikator segmentu. W trybie rzeczywistym jest to adres liniowy segmentu podzielony przez 16, a w trybie chronionym selektor.
CS - segment kodu,
DS - segment danych,
SS - segment stosu,
ES - dodatkowy segment danych,
FS - dodatkowy segment,
GS - dodatkowy segment.
Wskaźnik instrukcji
Rejestr EIP jest wskaźnikiem aktualnie wykonywanej instrukcji. Nie można się do niego odwołać bezpośrednio. W zależności czy segment jest typu USE32, czy USE16, używany jest rejestr 32-bitowy (EIP), lub rejestr 16-bitowy (IP).
Rejestr flag
Rejestr rozszerzonych flag EFLAGS, zawiera ogólne informacje o stanie procesora w danej chwili. Stan tych flag zmieniany jest w zależności od wyniku niektórych operacji. Rozmieszczenie flag w rejestrze przedstawia rysunek:
CF - (Carry Flag) znacznik przeniesienia,
PF - (Partity Flag) znacznik parzystości,
AF - (Auxiliary Flag) znacznik przeniesienia pomocniczego,
ZF - (Zero Flag) znacznik zera,
SF - (Sign Flag) znacznik znaku,
TF - (Trap Flag) znacznik pułapki,
IF - (Interrupt Enable) znacznik zezwolenia na przerwanie,
DF - (Direction Flag) znacznik kierunku operacji łańcuchowych,
OF - (Overflow) przepełnienie,
IOPL - (I/O Privillege Level) 2-bitowy poziom uprzywilejowania operacji wejścia wyjścia,
NT - (Nested Task) znacznik zagnieżdżonego zadania,
RF - (Resume Flag) znacznik wznowienia,
VM - (Virtual 8086 Mode) znacznik trybu V86.
Rejestry kontrolne
Rejestry kontrolne sterują pracą procesora. Wyróżniamy trzy rejestry CR0, CR2 i CR3, dwa ostatnie uwzględniane są tylko w przypadku włączonego mechanizmu stronicowania pamięci.
CR3 - bity 12-31 zawieraja numer (adres fizyczny podzielony przez 4k) strony zawierającej katalog stron, reszta bitów zarezerwowana,
CR2 - zawiera 32-bitowy adres liniowy komórki pamięci, do której odwołanie spowodowało wyjątek 0EH (Page Fault),
CR0 - rejestr flag kontrolujących pracę procesora, przedstawiony na rysunku:
PE - (Protection Enable) bit trybu chronionego,
MP - (Math Present) znacznik kontroli koprocesora,
EM - (Emulation) znacznik emulacji koprocesora,
TS - (Task Switched) znacznik przełączenia zadania,
ET - (Extension Type) znacznik typu koprocesora,
PG - (Paging) znacznik mechanizmu stronicowania.
Rejestry testowe
Rejestry TR6 i TR7 służą do testowania pamięci cache TLB (Translation Lookaside Buffer) używanej przy przekształcaniu przez procesor adresu liniowego na adres fizyczny. Są one specyficzne dla maszyn 80386 i w wyższych procesorach powinny być używane zgodnie z ich specyfikacją. Testowanie pamięci TLB powinno się odbywać na poziomie BIOSu, dlatego rejestry te nie zostaną dokładnie omówione.
Rejestry uruchomieniowe
Rejestry uruchomieniowe zwiększają możliwości kontroli kodu i danych. Za ich pomocą można ustawić cztery pułapki i kontrolować ich stan. Istnieje sześć rejestrów uruchomieniowych DR0, DR1, DR2, DR3, DR6 i DR7. Rejestry DR0-DR3, zawierają adresy liniowe pułapek, pozostałe dwa służą do konfiguracji i sprawdzania pułapek.
DR7 - rejestr flag kontrolujących pułapki, przedstawiony na rysunku:
L0-L3 - uaktywnienie pułapki 0-3, zerowane po wystąpieniu przełączenia zadania,
G0-G3 - uaktywnienie pułapki 0-3, nie zerowane po wystąpieniu przełączenia zadania,
LE - zwolnienie pracy procesora po wystąpieniu pułapki, zerowane po wystąpieniu przełączenia zadania,
GE - zwolnienie pracy procesora po wystąpieniu pułapki, nie zerowane po wystąpieniu przełączenia zadania,
R/W0-R/W3 - 2-bitowa wartość określająca warunek jaki wymusza przerwanie działania programu po wystąpieniu pułapki 0-3:
00 - wykonanie instrukcji,
01 - zapisanie,
11 - odczytanie lub zapisanie,
LEN0-LEN3 - 2-bitowa wartość określająca rozmiar pułapki 0-3:
00 - bajt (lub wykonanie instrukcji),
01 - słowo,
11 - podwójne słowo,
DR6 - rejestr flag kontrolujących stan pułapek, przedstawiony na rysunku. Bity tego rejestru mogą być jedynie ustawiane przez procesor, dlatego procedura obsługi wyjątku 01H (Debugger exceptions) powinna je wyzerować.
B0-B3 - natrafienie na pułapkę 0-3,
BD - znacznik kolizji w dostępie do rejestrów uruchomieniowych,
BS - znacznik związany z flagą TF rejestru EFLAGS,
BT - znacznik związany z bitem T rejestru TSS,
Rejestry zarządzania pamięcią
Istnieją jeszcze dodatkowe rejestry przechowujące adresy tablic systemowych GDTR, LDTR, IDTR, i TR:
GDTR - rejestr ten składa się z 6 bajtów w których przechowuje adres liniowy i rozmiar (pomniejszony o 1) globalnej tablicy deskryptorów GDT. Rejestr GDTR przedstawia poniższy rysunek:
LDTR - rejestr analogiczny do GDTR, zawiera informacje o lokalnej tablicy deskryptorów LDT,
IDTR - 16-bitowy rejestr przechowujący selektor tablicy deskryptorów przerwań IDT,
TR - 16-bitowy rejestr przechowujący selektor segmentu stanu zadania TSS.
3.3. Stronicowanie pamięci
Mechanizm stronicowania pamięci zaimplementowany został dopiero w procesorach 80386 i stanowił przełom w komputerach typu PC. Umożliwił on stworzenie trybu V86, sklejanie fizycznie rozproszonych obszarów pamięci w ciągłe bloki, oraz implementację pamięci wirtualnej. Mechanizm ten dzieli cały dostępny 4G obszar na strony wielkości 4kb, które za pomocą tablic stron zmieniają swoje położenie w przestrzeni adresowej widzianej przez programy.
Z mechanizmem stronicowania procesor związany jest przez flagę PG w rejestrze CR0 (włączającą/wyłączającą stronicowanie) oraz przez rejestry CR3 (zawierający fizyczny adres katalogu tablic) i CR2 (zawierający liniowy adres komórki pamięci do której dostęp wygenerował wyjątek). Przed ustawieniem flagi PG należy zainicjować katalog tablic i związane z nim tablice stron. Katalog tablic jest to struktura opisująca wszystkie tablice stron, które z kolei opisują strony fizyczne. Katalog i tablice mają rozmiar jednej strony (4kb), a każdy ich wpis zajmuje 4 bajty i opisuje jedną stronę. Łatwo więc policzyć że ilość tablic stron jest równa 1024, z których każda opisuje 1024 strony fizyczne. Tak więc mechanizm stronicowania potrafi opisać 1024 * 1024 * 4096 = 4G (ILOŚĆ_TABLIC * ILOŚĆ_STRON_OPISYWANYCH_PRZEZ_TABLICĘ * WIELKOŚĆ_STRONY), czyli cały obszar przestrzeni adresowej. O to jak wygląda pojedynczy wpis w katalogu i tablicach
stron:
P - (Present) bit obecności strony w pamięci (jeżeli wyzerowany reszta bitów do dyspozycji programisty),
R/W - (Read/Write) znacznik praw modyfikacji,
U/S - (User/Supervisor) znacznik praw dostępu,
A - (Accessed) ustawiany gdy wystąpił zapis lub odczyt ze strony,
D - (Dirty) ustawiany gdy wystąpił zapis do strony (nie dotyczy tablic stron),
AVAIL - (Available) trzy bity przeznaczone dla programisty,
Numer strony jest jej adresem fizycznym podzielonym przez 4k (4096).
Pozostaje tylko pytanie w jaki sposób powstaje adres liniowy do którego można się odwoływać. Na adres liniowy opisujący bajt pamięci składają się trzy człony:
numer wpisu w katalogu tablic (bity 22-31), który wyznacza tablicę stron,
numer wpisu w tablicy stron (bity 12-21), który wyznacza numer strony fizycznej,
przemieszczenie w stronie fizycznej (bity 0-11).
Translację adresu logicznego na adres fizyczny przedstawia rysunek:
3.3.1. Pamięć wirtualna
Dzięki mechanizmowi stronicowania pamięci możliwe jest stworzenie pamięci wirtualnej (virtual memory). Polega to na powiększeniu dostępnej przestrzeni o nieistniejące obszary pamięci. W chwili gdy wystąpi żądanie przydziału pamięci, a wykorzystany jest cały obszar fizyczny, system operacyjny zapisuje fragmenty pamięci fizycznej na dysk po czym przepina (za pomocą mechanizmu stronicowania) tą pamięć udostępniając ją żądającemu zadaniu. Jeżeli pamięć zapisana na dysk jest potrzebna, system zapisuje inny obszar i tak w kółko. Zapis dokonywany jest do specjalnego pliku wymiany (swap file), lub partycji wymiany (swap partition). Jeżeli jest to plik, musi on zajmować kolejne sektory i nie może być przenoszony w inne miejsce dysku. Rozmiar pamięci wirtualnej jest ograniczony przez rozmiar pliku lub partycji.
Pamiętać należy, że niektóre obszary pamięci powinny być zablokowane przed wymianą na dysk (dotyczy to głównie procedur obsługi przerwań, oraz procedur, które nie mogą czekać na stosunkowo długi czas odczytu z dysku).
Aby zrealizować czynność wymiany musimy mieć kontrolę nad wyjątkiem 0DH (Page fault), oraz nad tablicami stron. Należy zdefiniować jeden z bitów wpisu w tablicach, który będzie oznaczał czy strona może być wymieniona na dysk, czy nie. Ponieważ podczas, gdy bit P wpisu jest wyzerowany reszta bitów jest dostępna, należy je wykorzystać na numer sektora w pliku/partycji wymiany.
Procedura obsługi wyjątku 0DH musi w pierwszej kolejności odczytać adres liniowy komórki do której dostęp spowodował wyjątek (rejestr CR2). Na podstawie tego adresu znaleźć odpowiedni wpis w tablicach. Następnie trzeba odszukać stronę którą zapiszemy na dysk. Strona ta musi być obecna w pamięci (P=1) i musi mieć prawo do wymiany (zdefiniowany przez nas bit). Zapisujemy stronę na dysk, zerujemy bit P i uzupełnianiu jej wpis o sektor w pliku/partycji wymiany. Następnie wolną już stronę fizyczną podpinamy pod tą która wygenerowała wyjątek i na koniec i odczytujemy dane z dysku.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
3.3.2. TLB
TLB (Translation Lookaside Buffer), jest blokiem pamięci wewnętrznej procesora, który służy przyspieszeniu mechanizmu stronicowania. Zawiera on dane na temat najczęściej używanych tablic stron. Bufor ten powinien być czyszczony po każdej zmianie zawartości katalogu, lub tablic stron, poprzez załadowanie rejestru CR3. Można go także testować za pomocą rejestrów TR6 i TR7.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
pre {font-family: Courier}
3.4. Tryb rzeczywisty
Tryb rzeczywisty jest już bardzo przestarzały i istnieje jedynie ze względu na zgodność z dużą ilością oprogramowania przeznaczonego wyłącznie dla niego. Tryb ten może zaadresować wyłącznie 1Mb pamięci operacyjnej. 20-bitowy adres fizyczny jest dostępny poprzez dwa rejestry: segmentowy i indeksowy. Rejestr segmentowy zawiera adres pamięci wyrażony w paragrafach (paragraf=16 bajtów), natomiast rejestr indeksowy zawiera przemieszczenie od adresu wskazywanego przez rejestr segmentowy, wyrażone w bajtach. Adres taki ma postać SEGMENT:OFFSET (offset czyli przemieszczenie) i nazywany jest adresem logicznym. Adres fizyczny otrzymuje się z adresu logicznego według wzoru: ADRES_FIZYCZNY = SEGMENT * 16 + OFFSET. Ponieważ obydwa rejestry segmentowy i indeksowy są 16-bitowe widać że w ten sposób można zaadresować ponad 1Mb pamięci (0FFFFH * 16 + 0FFFFH = 10FFEFH czyli 1,114,096 bajtów). Dowolna komórka pamięci może być adresowana na wiele sposobów np. komórka o adresie fizycznym 012CH może być wyrażona za pomocą nast. adresów logicznych: 0012H:000CH, 0010H:002CH, 0011H:001CH lub 0000H:012CH. Widać więc że segment nie jest jakąś sztywną formą podziału pamięci, a jego rozmiar 64kb jest ograniczony przez wielkość rejestru indeksowego.
Uwaga! Pomimo iż procesory od 386 wzwyż posiadają 32-bitowe rejestry indeksowe w trybie rzeczywistym mechanizm segmentacji używa tylko ich mniej znaczącego słowa.
W trybie rzeczywistym mechanizm stronicowania jest wyłączony dlatego adres logiczny jest zamieniany wprost na adres fizyczny (bez pośredniej zamiany na adres liniowy).
Komputery PC posiadają następujące rejestry segmentowe: CS, DS, ES, SS (procesory od 386 wzwyż dodatkowo FS i GS), oraz indeksowe: IP, SI, DI, SP, BP. Oznaczenia rejestrów segmentowych nazywamy prefiksami. Rejestry segmentowe i indeksowe tworzą domyślne pary (przy odwołaniu do danego rejestru indeksowego nie trzeba podawać ich domyślnego prefiksu): DS:SI, ES:DI, SS:BP. Do rejestrów IP i SP nie odwołujemy się jawnie, poza tym rejestr IP jest wskaźnikiem wykonywanej instrukcji i nie możemy go modyfikować poleceniem MOV.
Ze względu na niewielki w dzisiejszych czasach rozmiar segmentu stosuje się różne techniki odczytu danych. Poniższy kod przedstawia najprostszy sposób odczytu następujących po sobie segmentów których liczba znajduje się w rejestrze CX.
;w CX ilość segmentów do odczytu
;początek bufora ma adres DS:0000H
XOR SI,SI
CLD
PETLA:
LODSB
;tutaj obróbka danej znajdującej się w AL
OR SI,SI
JNE SHORT PETLA
MOV DX,DS
ADD DH,10h
MOV DS,DX
LOOP PETLA
;tutaj zakończenie odczytu z bufora
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
3.5. Tryb chroniony
Tryb chroniony istnieje począwszy od procesorów 80286 gdzie pozwalał zaadresować 16Mb pamięci, jednak pełne skrzydła rozwinął dopiero w procesorze 80386 i obecnie udostępnia nam przestrzeń adresową wielkości 4Gb. Tryb chroniony maszyn 80286 jest zubożoną wersją w stosunku do 80386, więc nie będziemy się nim dokładnie zajmować.
Dzięki wprowadzeniu trybu chronionego możliwe się stało stworzenie profesjonalnych, wielozadaniowych systemów operacyjnych, zorientowanych sieciowo. Mechanizmy ochrony zapewniają łatwe wykrywanie błędów i bezpieczeństwo danych systemowych (system może mieć architekturę całkowicie zamkniętą i udostępniać programom zasoby komputera według własnych praw). Wielozadaniowość pozwala na jednoczesne wykonywanie się niezależnie wielu zadań, którymi zarządza system operacyjny. W połączeniu ze stronicowaniem pamięci tryb chroniony czyni maszyny klasy PC najpoważniejszymi komputerami osobistymi.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
3.5.1. Organizacja pamięci
W trybie chronionym pamięć podzielona jest na segmenty, z których każdy może mieć dowolny rozmiar i mogą one na siebie nachodzić. Segmenty te nie mają jednak nic wspólnego z segmentami trybu rzeczywistego, dlatego też oprogramowanie jest specyficzne dla danego trybu i nie może być uruchamiane w innym. Segmenty opisywane są za pomocą deskryptorów, umieszczanych w tablicach deskryptorów. Istnieją trzy rodzaje takich tablic:
- globalna tablica deskryptorów GDT (Global Descriptor Table), jest tablicą przeznaczoną dla systemu operacyjnego. Dane na jej temat zawiera rejestr GDTR,
- lokalna tablica deskryptorów LDT (Local Descriptor Table), jest tablicą specyficzną dla danego zadania, przy przełączaniu zadań następuje też przełączenie lokalnych tablic. Jej selektor zawiera rejestr LDTR,
- tablica deskryptorów przerwań IDT (Interrupt Descriptor Table), jest tablicą opisującą procedury obsługi przerwań i wyjątków, przy wystąpieniu przerwania procesor odczytuje dane z tej tablicy. Dane na jej temat zawiera rejestr IDTR.
Deskryptory dzielimy na deskryptory segmentów i deskryptory bramek. Pierwsze opisują segmenty pamięci, drugie punkty wejścia do segmentów (deskryptory bramek opisane są szerzej w kolejnych tematach). Wyróżniamy trzy rodzaje deskryptorów segmentów.
deskryptor segmentu danych - opisuje obszary w których mogą być zapisywane i czytane dane (również stos):
deskryptor segmentu kodu - opisuje obszary gdzie znajduje się wykonywalny kod programów, mogą być z nich również czytane dane:
deskryptor segmentu systemowego - opisuje takie struktury jak lokalna tablica deskryptorów i segment stanu zadania:
Oto opis pól wszystkich deskryptorów segmentów:
A - (Accessed) ustawiany gdy selektor segmentu jest ładowany do rejestru segmentowego,
W - (Writable) określa, czy może wystąpić operacja zapisu do segmentu danych,
R - (Readable) określa, czy może wystąpić operacja odczytu z segmentu kodu,
E - (Expand-down) określa kierunek ekspansji segmentu danych (używany do tworzenia segmentów stosu),
C - (Confirming) określa czy segment kodu jest dostępny na wszystkich poziomach uprzywilejowania,
P - (Segment Present) określa, czy segment jest obecny w pamięci,
AVL - (Available) bit dostępny dla programisty,
B - (Big) określa rozmiar segmentu (zwykle ustawiony, gdy bit G=1),
D - (Default) określa domyślny rozmiar adresów w obrębie segmentu (0 - adresy 16-bitowe, 1 - adresy 32-bitowe),
G - (Granularity) określa, czy limit segmentu jest wyrażony w bajtach (G=0), czy w 4k stronach (G=1),
DPL - (Descriptor Privillege Level) 2-bitowa wartość określająca poziom uprzywilejowania deskryptora.
Adres bazowy jest 32-bitowym adresem liniowym wskazującym początek segmentu.
Limit jest rozmiarem segmentu pomniejszonym o 1 (jeżeli bit G=1 rozmiar jest pomniejszony o 4kb). Jeżeli bit E jest wyzerowany poprawne adresy w obrębie segmentu są z zakresu 0 do LIMIT + 1, jeżeli jest ustawiony z zakresu MAKSYMALNY_ROZMIAR - (LIMIT + 1) do MAKSYMALNY_ROZMIAR (przydatne jest to w segmentach stosu, gdyż nie trzeba zmieniać wartości szczytu stosu przy każdej zmianie jego wielkości). MAKSYMALNY_ROZMIAR zależy od bitu G i może być równy 64k lub 4G.
Typy segmentów systemowych przedstawiają poniższe wartości:
01H - wolny segment stanu TSS (dla procesora 286),
02H - lokalna tablica deskryptorów LDT,
03H - zajęty segment stanu TSS (dla procesora 286),
09H - wolny segment stanu TSS (dla procesora 386),
0BH - zajęty segment stanu TSS (dla procesora 386),
pozostałe wartości są zarezerwowane, lub wykorzystywane przez bramki.
Dostęp do obszarów zdefiniowanych przez deskryptory jest realizowany poprzez selektory. Selektor wskazuje na konkretny deskryptor w tabeli GDT lub LDT, a jego wartość jest umieszczana w jednym z rejestrów segmentowych. Selektor ma następującą postać:
TI - (Table Indicator) oznacza rodzaj tabeli (0 -GDT, 1 - LDT),
RPL - (Requestor Privillege Level) określa poziom uprzywilejowania kodu który żąda dostępu.
Numer deskryptora jest przemieszczeniem w tablicy podzielonym przez 8.
Model "flat"
Niektóre środowiska systemowe nie współpracują z segmentową organizacją pamięci, a wszelkie wykorzystywane adresy są liniowe. Taki model pamięci nazywamy modelem "flat". Jest on dosyć prosty do zaimplementowania w trybie chronionym. Należy po prostu podczas inicjacji systemu zdefiniować segmenty o adresie bazowym 0 i limicie 4Gb (osobno segment kodu i osobno danych) po czym załadować rejestry segmentowe odpowiednimi selektorami. Model "flat" pozbawia nas jednak funkcjonalności mechanizmów ochrony pamięci.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
3.5.2. Ochrona pamięci
Jednym z założeń trybu chronionego jest ochrona pamięci przed niepożądanym zapisem, odczytem i wykonywaniem kodu. Istnieją dwa poziomy ochrony pamięci: ochrona na poziomie segmentacji i ochrona na poziomie stronicowania. Drugi poziom występuje wyłącznie w przypadku włączonego mechanizmu stronicowania, inaczej jest ignorowany.
Ochrona na poziomie segmentacji
Ochrona na tym poziomie obejmuje:
- sprawdzanie typów segmentów,
- sprawdzanie limitu,
- ograniczenie dostępu do segmentów,
- ograniczenie dostępu przestrzeni,
- ograniczenie wykonywania instrukcji.
Sprawdzanie typów segmentów dotyczy praw zapisu/odczytu/uruchamiania. Ma ono znaczenie gdy na przykład selektor segmentu kodu jest ładowany do rejestru segmentowego CS. Niektóre rejestry segmentowe mogą przechowywać selektory tylko niektórych segmentów. Wyjątek 0Dh (General Protection Fault) jest generowany podczas wykonania złej operacji na danym segmencie.
Sprawdzanie limitu występuje podczas realizowania dostępu do komórki pamięci. Jeżeli podane przemieszczenie wykracza ponad limit segmentu generowany jest wyjątek 0DH.
Ograniczenie dostępu do segmentów jest realizowane poprzez poziomy uprzywilejowania. Poziom uprzywilejowania to dwubitowa wartość, przy czym najwyższy poziom to 0, a najniższy 3. Są one przechowywane w polach CPL (Current Privillege Level), RPL (Requestor Privillege Level) i DPL (Descriptor Privillege Level). CPL jest aktualnym poziomem uprzywilejowania znajdującym się w selektorze wykonywanego kodu, RPL znajduje się w selektorze odwołującym się do danego segmentu a DPL w dekryptorze tego segmentu. Procesor bierze pod uwagę wszystkie te pola i jeżeli stwierdzi że dany kod nie ma dostępu do segmentu, generuje wyjątek 0DH. Dany kod ma dostęp do segmentów danych na tym samym lub niższym poziomie, lecz może wykonywać skoki tylko do segmentów kodu na tym samym poziomie. Nie dotyczy to zgodnych segmentów kodu (bit C w deskryptorze segmentu kodu). Aby umożliwić wykonywanie kodu na innym poziomie stworzone zostały bramki. Bramki definiują punkt wejścia do segmentów kodu. Na ich potrzeby stworzone zostały specjalne deskryptory bramek. Wyróżniamy cztery typy bramek:
- bramki wywołania,
- bramki pułapek,
- bramki przerwań,
- bramki zadań.
Bramki zadań omówione są przy zagadnieniu wielozadaniowości, a bramki pułapek i przerwań przy przerwaniach i wyjątkach.
Deskryptory bramek wywołania mogą być umieszczone w tablicach GDT i LDT. Program na poziomie uprzywilejowania większym lub równym poziomowi bramki może za pomocą instrukcji CALL przenieść wykonywanie programu do segmentu innego poziomu (wykorzystywany jest do tego tylko selektor bramki, przemieszczenie jest pomijane). Poniżej ukazany jest deskryptor bramki wywołania:
P - (Present) bit obecności segmentu do którego odwołuje się bramka,
DPL - (Descriptor Privillege Level) określa, najniższy poziom uprzywilejowania jaki musi mieć kod aby wykonać skok przez bramkę.
Selektor musi wskazywać segment do którego ma nastąpić skok, a przemieszczenie jest adresem relatywnym w obrębie tego segmentu. Deskryptor zawiera też liczbę słów jaka ma być skopiowana ze stosu (każdy poziom uprzywilejowania ma oddzielny stos).
Ograniczenie dostępu do przestrzeni wejścia/wyjścia jest realizowane przez wartość IOPL (Input/Output Privillege Level) w rejestrze flag oraz przez mapy portów w segmentach stanu zadań. Pole IOPL to określa jaki najniższy poziom uprzywilejowania może mieć kod, aby wykonać operację wejścia/wyjścia. Mapy bitów w segmentach TSS określają dokładnie które z portów może używać zadanie (jedynka zabrania użycia portu). Mapy te sprawdzane są tylko w przypadku, gdy zadanie ma niższy priorytet niż określa IOPL. W wypadku wystąpienia niepożądanego dostępu do przestrzeni we/wy procesor generuje wyjątek 0DH.
Ograniczenie wykonywania instrukcji dotyczy tylko instrukcji uprzywilejowanych. W wypadku użycia ich na poziomie niższym niż 0 generowany jest wyjątek 0DH. Do instrukcji uprzywilejowanych zaliczamy:
- CLTS - wyzerowanie bitu przełączenia zadania TS w rejestrze EFLAGS,
- HLT - zatrzymanie pracy procesora do momentu wystąpienia przerwania,
- LGDT, LIDT, LLDT - załadowanie rejestrów tablic systemowych,
- LMSW - załadowanie słowa stanu procesora,
- LTR - załadowanie rejestru zadania,
- MOV do/z CRn, DRn, TRn - załadowanie, lub odczytanie rejestrów kontrolnych, uruchomieniowych i testowych.
Ochrona na poziomie stronicowania
Ochrona na tym poziomie obejmuje:
- ograniczenie dostępu do stron,
- sprawdzanie typów stron.
Ograniczenie dostępu do stron jest związane z bitem U/S wpisu w tablicy stron. Określa on dwa poziomy dostępu do stron: supervisor (poziom uprzywilejowania 0-2) i user (poziom uprzywilejowania 3). Jeżeli kod żądający dostępu do strony jest na prawach suprvisor, może on dowolnie odczytywać, lub zapisywać stronę (pomijany jest bit R/W).
Sprawdzanie typów stron dotyczy praw zapisu/odczytu (bit R/W wpisu w tablicy stron). Jeżeli nieodpowiednia operacja jest wykonana na stronie, generowany jest wyjątek.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
3.5.3. Wielozadaniowość
Tryb chroniony umożliwia nam zaimplementowanie mechanizmu wielozadaniowości poprzez wprowadzenie specjalnych segmentów stanu zadania TSS (Task State Segment). Selektor aktualnie wykonywanego zadania jest umieszczony w rejestrze zadania TR (Task Register). System operacyjny dokonuje przełączania zadań gdy wystąpi przerwanie zegarowe, lub gdy otrzyma od zadania sygnał bezczynności (IDLE). Mechanizm wielozadaniowości jest ściśle powiązany z bitem NT rejestru EFLAGS i bitem TS rejestru CR0. Pierwszy z nich oznacza zagnieżdżone zadanie tzn. ustawinay jest gdy jedno zadanie zostało przerwane przez drugie (zapobiega to zapętleniu się zadań). Drugi z bitów ustawiany jest gdy wystąpiła operacja przełączania zadań.
Rejestr TSS przechowuje wszelkie dane dotyczące zadania, włączając adres katalogu stron (każde zadanie może mieć inne odwzorowanie adresów liniowych w fizyczne), selektor tablicy LDT oraz mapę dostępnych portów. Rozmiar segmentu TSS wynosi minimum 104 bajty, lecz może być większy w zależności od wielkości mapy portów i danych przechowywanych przez system. Postać segmentu TSS przedstawia rysunek:
T - (Trap) oznacza, czy ma być wygenerowany wyjątek 01H (Debug exceptions) przy przełączaniu do danego zadania.
Oprócz rejestrów segment TSS zawiera selektor poprzedniego segmentu stanu (uzupełniany przez procesor w wypadku przerwania działania poprzedniego zadania), oraz przemieszczenie wskazujące na mapę portów. Mapa ta jest strukturą w której jeden bit oznacza jeden z 65536 portów (jedynka oznacza blokadę). Mapa portów nie musi uwzględniać ich wszystkich, tylko pewną ilość numerowaną kolejno od 0 do n.
Segment TSS ma swój specjalny deskryptor, które może występować jedynie w tablicy GDT. Nie może on służyć do modyfikowania zawartości segmentu (w tym celu należy stworzyć tożsamy segment danych). W procesorach 80386 ma on następującą postać:
B - (Busy) oznacza, czy zadanie jest aktualnie wykonywane (ustawiany i zerowany przez procesor).
Pozostałe bity mają takie samo znaczenie jak w innych deskryptorach.
Na potrzeby wielozadaniowości stworzona została też specjalna bramka zwana bramką zadania. Można ją umieszczać we wszystkich tablicach systemowych. Pozwala ona programom nie mającym dostępu do rejestru TR na zmianę zadania. Deskryptor bramki zadania wygląda następująco:
Selektor wskazuje na segment stanu zadania. Pozostałe bity jak w przypadku bramki wywołania.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
3.5.4. Przerwania i wyjątki
W trybie chronionym istnieje tablica IDT (Interrupt Descriptor Table) opisująca wszystkie 256 przerwań procesora. Deskryptorami tej tablicy mogą być bramki zadań, bramki pułapek i bramki przerwań. Deskryptor bramki przerwania wygląda następująco:
A oto jak wygląda deskryptor bramki pułapki:
Wszystkie bity mają takie samo znaczenie jak w przypadku bramki wywołania.
Bramki przerwań i pułapek mają bardzo podobne znaczenie. Wykonują one skok pod zadany adres (wykonywane są w kontekście zadania w którym wystąpiły), zapisują na stosie rejestr EFLAGS i zerują bit TF (aby uniemożliwić krokowe śledzenie procedury obsługi). Różnica pomiędzy nimi polega na tym, że bramka przerwania zeruje dodatkowo bit IF (wyłącza przerwania). Powrót w obydwu przypadkach powinien być realizowany instrukcją IRET.
Wyjątki
Wyjątki są zestawem przerwań numerowanych od 00H do 1FH. Zarezerwowane są one dla procesora i generowane są w przypadku szczególnych zdarzeń związanych z jego pracą. Wyjątki w odróżnieniu od pozostałych przerwań nie mogą być maskowane instrukcją CLI. Dzielą się one na:
- niepowodzenia, generowane przed wykonaniem instrukcji, umożliwiają powrót i ponowne jej wykonanie,
- pułapki, generowane po wykonaniu oczekiwanej instrukcji,
- załamania, generowane np. po wykryciu błędu w tablicach systemowych, umożliwiają lokalizację instrukcji jak również ponowne załadowanie programu którego działanie spowodowało wyjątek.
Oto lista wyjątków procesora 80386 wraz z krótkim opisem:
numer |
nazwa |
opis wyjątku |
00H |
Divide error |
Dzielenie przez zero |
01H |
Debug exceptions |
Wyjątek generowany przez pułapki |
02H |
Nonmaskable interrupt |
Przerwanie niemaskowalne (NMI) |
03H |
Breakpoint |
Pułapka (INT 3) |
04H |
Overflow |
Nadmiar (INTO) |
05H |
Bounds check |
Sprawdzenie limitu (BOUND) |
06H |
Invalid opcode |
Nieprawidłowa instrukcja |
07H |
Coprocessor not availabe |
Koprocesor nieobecny |
08H |
Double fault |
Niepowodzenie wykonania procedury wyjątku |
09H |
(reserved) |
Zarezerwowane |
0AH |
Invalid TSS |
Błędny segment stanu zadania |
0BH |
Segment not present |
Nieobecność segmentu w pamięci |
0CH |
Stack exception |
Przekroczenie limitu stosu |
0DH |
General protection fault |
Błąd naruszający mechanizm ochrony |
0DH |
Page fault |
Wyjątek generowany przez mechanizm stronicowania |
0FH |
(reserved) |
Zarezerwowane |
10H |
Coprocessor error |
Błąd koprocesora |
11H-1FH |
(reserved) |
Zarezerwowane |
Ze względu na to iż podstawowy układ przerwań sprzętowych PIC (Programable Interrupt Controler) ma standardowe wektory w obszarze przerwań 08H-0FH (zarezerwowanych dla wyjątków procesora), należy go przeprogramować przed przełączeniem do trybu chronionego, w celu uniknięcia konfliktów.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
3.6. Tryb V86
Tryb V86 (Virtual 8086 machine) jest możliwy do zrealizowania przez połączenie trybu rzeczywistego, trybu chronionego i stronicowania pamięci. Specjalizowany program (może być to system operacyjny) pracuje w trybie chronionym z którego rozporządza zadaniami wykonującymi się w trybie rzeczywistym. Dzięki stronicowaniu, może on udostępniać programom trybu rzeczywistego pamięć rozszerzoną przez podczepianie jej w obszar 1Mb. Należy zatem pamiętać, i uwzględniać w przekazywaniu adresów do urządzeń, że w przypadku trybu V86 adresy logiczne zamieniane są na adresy liniowe, a te dopiero przez mechanizm stronicowania na adresy fizyczne. Program może sprawdzać czy pracuje w trybie rzeczywistym czy V86, przez kontrolę bitu VM w rejestrze EFLAGS.
Zalety trybu V86 wykorzystują wszelkie sterowniki pamięci EMS, jak również wielozadaniowe systemy operacyjne (np. Windows), które emulują środowisko systemu DOS.
4. Pamięć bazowa
Korzenie komputerów klasy PC sięgają czasów, kiedy 640kb pamięci operacyjnej wydawało się być w zupełności wystarczające i to na długie lata. Stąd też wynika architektura ówczesnych maszyn (komputery klasy XT), których rejestry pozwalały zaadresować maksymalnie do 1Mb pamięci. Aby załatać tę lukę w rozwoju komputerów, wprowadzono tryb chroniony pozwalający w modelach AT zaadresować do 16Mb, a w 386 i wyższych do 4Gb pamięci. System DOS pracujący w trybie rzeczywistym nie potrafi obsłużyć dodatkowej pamięci, dlatego też powstały liczne rozszerzenia udostępniające pamięć rozszerzoną (ponad 1 Mb) dla programów dosowych. Stąd też w przypadku DOSu pamięć dzielimy na bazową (base memory) i rozszerzoną (extended memory). Niniejszy rozdział traktuje o pierwszej z nich.
4.1. Pamięć konwencjonalna
Pamięć konwencjonalna (conventional memory) jest to pamięć o wielkości 640kb. Powstała ona przy tworzeniu pierwszych modeli komputera PC jako cała dostępna adresowalna pamięć, stąd wynika jej odrębność od pozostałych obszarów. Istotnymi elementami pamięci konwencjonalnej jest tablica wektorów przerwań oraz obszar zmiennych BIOS-u. Poza nimi znajdują się w niej procedury systemowe, programy rezydentne, oraz aktualnie wykonywany program. Obecnie, ze względu na niewielki obszar tej pamięci dąży się do jak największego udostępnienia jej programom poprzez ładowanie rezydentów i procedur systemowych do pamięci górnej i podwyższonej.
Do zarządzania pamięcią konwencjonalną służą nam funkcje 48H-4AH i 58H przerwania 21H. W szczególnych przypadkach funkcje te mogą obsługiwać również bloki pamięci górnej.
INT 21H, AH=48H, Przydzielenie bloku pamięci
Wejście:
BX = rozmiar bloku w paragrafach
Wyjście jeżeli CF=0:
AX:0000 = adres przydzielonego bloku
Wyjście jeżeli CF=1:
AX = kod błędu
BX = rozmiar największego wolnego bloku
INT 21H, AH=49H, Zwolnienie bloku pamięci
Wejście:
ES = segment przydzielonego bloku
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
AX = kod błędu
INT 21H, AH=49H, Zmiana rozmiaru bloku pamięci
Wejście:
ES = segment przydzielonego bloku
BX = nowy rozmiar bloku w paragrafach
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
AX = kod błędu
BX = największy możliwy rozmiar bloku
INT 21H, AX=5800H, Pobranie strategii alokacji pamięci
Wejście:
Nie ma
Wyjście:
CF = 0
AX = kod strategii alokacji pamięci
INT 21H, AX=5801H, Ustalenie strategii alokacji pamięci
Wejście:
BX = kod strategii alokacji pamięci
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
AX = kod błędu
A oto jak wygląda kod strategii alokacji pamięci:
Bity 0-1:
00 = alokacja w obszarze najniższym
01 = alokacja w obszarze najlepiej pasującym
10 = alokacja w obszarze najwyższym
Bity 6-7:
00 = alokacja wyłącznie pamięci konwencjonalnej
01 = alokacja wyłącznie pamięci górnej
10 = alokacja pamięci górnej i konwencjonalnej
Reszta bitów równa 0
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
pre {font-family: Courier}
4.1.1. Tablica wektorów przerwań
Tablica wektorów wszystkich 256 przerwań znajduje się pod adresem zerowym i zajmuje obszar 1kb, gdyż każdy wektor mieści się w 4 bajtach (2 na segment i 2 na przemieszczenie). Podczas każdego wywołania przerwania procesor odkłada na stos rejestr flagowy, oraz rejestry CS i IP (aktualną pozycję wykonywanego programu). Następnie oblicza pozycję w tablicy wektorów (numer przerwania * 4) i wykonuje daleki skok pod otrzymany adres. W trybie chronionym tablica wektorów przerwań zastąpiona została przez tablicę deskryptorów przerwań, która może znajdować się gdziekolwiek w pamięci. Jej adres umieszczony jest w rejestrze IDTR (Interrupt Descriptor Table Register).
Podmianę wektora możemy zrealizować poprzez funkcje DOSu, lub też ręcznie przez bezpośredni dostęp do tablicy wektorów (przy drugim sposobie należy koniecznie pamiętać o wyłączeniu przerwań na czas podmiany). Oto funkcje 35H i 25H przerwania 21H, które zajmują się obsługą wektorów:
INT 21H, AH=35H, Pobranie wektora przerwania
Wejście:
AL = numer wektora przerwań
Wyjście:
ES:BX = adres procedury obsługi przerwania
INT 21H, AH=25H, Ustawienie wektora przerwania
Wejście:
AL = numer wektora przerwań
DS:DX = adres procedury obsługi przerwania
Wyjście:
Nie ma
Poniższy fragment kodu obrazuje jak podmienić wektor bez informowania systemu, a następnie przywrócić poprzedni:
;nowy wektor znajduje się w rejestrach AX:DX
;w BX znajduje się numer przerwania
.CODE
;podmiana wektora
SHL BX,2
MOV ES,0
CLI
XCHG DX,[ES:BX]
XCHG AX,[ES:BX+2]
STI
MOV [STARY],DX
MOV [STARY+2],AX
;przywrócenie wektora
MOV DX,[STARY]
MOV AX,[STARY+2]
CLI
MOV [ES:BX],DX
MOV [ES:BX+2],AX
STI
.DATA
;miejsce na stary wektor
STARY DW ?,?
Oto lista wszystkich przerwań wraz z przemieszczeniem w tablicy wektorów.
numer |
offset |
opis przerwania |
Przerwania BIOSu |
|
|
0H |
0000H |
Dzielenie przez zero |
1H |
0004H |
Praca krokowa |
2H |
0008H |
Przerwanie niemaskowalne (NMI) |
3H |
000CH |
Pułapka |
4H |
0010H |
Nadmiar |
5H |
0014H |
Drukowanie ekranu |
6H-7H |
0018H |
Zarezerwowane |
8H |
0020H |
IRQ 0 - Przerwanie zegarowe |
9H |
0024H |
IRQ 1 - Przerwanie klawiatury |
0AH |
0028H |
IRQ 2 - Zarezerwowane |
0BH |
002CH |
IRQ 3 - Przerwanie łącza szeregowego (COM2) |
0CH |
0030H |
IRQ 4 - Przerwanie łącza szeregowego (COM1) |
0DH |
0034H |
IRQ 5 - Przerwanie drukarki (LPT2) |
0EH |
0038H |
IRQ 6 - Przerwanie sterownika dyskietek |
0FH |
003CH |
IRQ 7 - Przerwanie drukarki (LPT1) |
10H |
0040H |
Obsługa karty graficznej |
11H |
0044H |
Odczyt konfiguracji systemu |
12H |
0048H |
Odczyt wielkości pamięci konwencjonalnej |
13H |
004CH |
Obsługa dysków |
14H |
0050H |
Obsługa portu szeregowego |
15H |
0054H |
Różnorodne funkcje |
16H |
0058H |
Obsługa klawiatury |
17H |
005CH |
Obsługa drukarki |
18H |
0060H |
Wywołanie programu konfiguracyjnego BIOSu. |
19H |
0064H |
Uruchomienie bootstrap |
1AH |
0068H |
Obsługa zegara |
1BH |
006CH |
Obsługa klawisza CTRL-BREAK |
1CH |
0070H |
Obsługa przerwania zegara czasu rzeczywistego |
1DH |
0074H |
Parametry sterownika graficznego ekranu 6845 |
1EH |
0078H |
Parametry napędu dyskietek |
1FH |
007CH |
Wskaźnik do rozszerzonych znaków ASCII |
Przerwania DOSu |
|
|
20H |
0080H |
Zakończenie programu |
21H |
0084H |
Uniwersalne funkcje systemowe |
22H |
0088H |
Adres zakończenia programu (procesu) |
23H |
008CH |
Obsługa przerwania wykonywania programu |
24H |
0090H |
Obsługa błędów krytycznych |
25H |
0094H |
Odczyt sektorów logicznych dysku |
26H |
0098H |
Zapis sektorów logicznych dysku |
27H |
009CH |
Zakończenie programu z pozostawieniem w pamięci |
28H |
00A0H |
Praca w tle |
29H |
00A4H |
Szybkie wysłanie pojedynczego znaku |
2AH |
00A8H |
Nieudokumentowane |
2BH-2DH |
00ACH |
Zarezerwowane |
2EH |
00B8H |
Nieudokumentowane |
2FH |
00BCH |
Funkcje XMS i inne |
30H |
00C0H |
CP/M JMP Command |
31H |
00C4H |
Funkcje DPMI |
32H |
00C8H |
Zarezerwowane |
33H |
00CCH |
Obsługa myszy |
34H-3FH |
00D0H |
Zarezerwowane |
40H |
0100H |
Opis dyskietki |
41H |
0104H |
Opis dysku twardego nr 1 |
42H |
0108H |
Przerwanie karty EGA/VGA |
43H |
010CH |
Wskaźniki parametrów inicjujących |
44H |
0110H |
Zarezerwowane dla Novell NetWare |
45H |
0114H |
Zarezerwowane |
46H |
0118H |
Opis dysku twardego nr 2 |
47H-5FH |
011CH |
Zarezerwowane |
60H-63H |
0180H |
Zarezerwowane dla użytkownika |
64H |
0190H |
Zarezerwowane dla Novell NetWare |
65H-66H |
0194H |
Zarezerwowane dla użytkownika |
67H |
019CH |
Funkcje VCPI |
68H-6CH |
01A0H |
Zarezerwowane |
6DH |
01B4H |
Zarezerwowane dla karty VGA |
6EH-6FH |
01B8H |
Zarezerwowane |
70H |
01C0H |
IRQ 8 - Przerwanie zegara czasu rzeczywistego |
71H-74H |
01C4H |
IRQ 9 - IRQ 12 - Zarezerwowane dla dodatkowych urządzeń |
75H |
01D4H |
IRQ 13 - Przerwanie koprocesora |
76H |
01D8H |
IRQ 14 - Przerwanie sterownika dysków twardych |
77H |
01DCH |
IRQ 15 - Zarezerwowane dla dodatkowych urządzeń |
78H-7FH |
01E0H |
Zarezerwowane |
80H-85H |
0200H |
Zarezerwowane dla BASICa |
86H-F0H |
0218H |
Przerwania używane przez BASIC |
F1H-FDH |
0384H |
Zarezerwowane dla użytkownika |
FEH-FFH |
03F8H |
Zarezerwowane |
4.1.2. Zmienne BIOSu
Zmienne BIOSu są obszarem pamięci o wielkości 256 bajtów i zaczynają się od adresu fizycznego 0400H (adres logiczny 0040H:0000H). Zmienne te można odczytywać i modyfikować, należy jednak zauważyć że niektóre z nich zmieniane są przez przerwania obsługiwane przez BIOS.
Poniżej znajduje się lista wszystkich zmiennych BIOSu wraz z przemieszczeniem od adresu 0400H.
offset |
opis zmiennej |
0000H |
Adresy we/wy COM1-COM4 |
0008H |
Adresy we/wy LPT1-LPT4 |
0010H |
Słowo konfiguracji systemu |
0012H |
Bajt zarezerwowany dla inicjacji komputera |
0013H |
Słowo określające wielkość pamięci RAM w kb |
0015H |
Bajt zarezerwowany przy testowaniu komputera |
0016H |
Bajt kodów błędów wykrytych podczas inicjacji komputera |
0017H |
Słowo stanu klawiatury |
0019H |
Zmienna używana przy wpisywaniu kodu z klawiatury numerycznej i klawisza ALT |
001AH |
Początek bufora klawiatury |
001CH |
Koniec bufora klawiatury |
001EH |
32 bajtowy bufor klawiatury |
003EH |
Bajt rekalibracji dysku miękkiego |
003FH |
Bajt stanu silników napędów dyskowych |
0040H |
Licznik czasu włączenia silnika |
0041H |
Bajt stanu dyskietki |
0042H |
Siedem bajtów sterownika dyskietki |
0049H |
Bieżący tryb pracy sterownika ekranu |
004AH |
Liczba kolumn na ekranie |
004CH |
Wielkość pamięci obrazu w bajtach |
004EH |
Adres początku obrazu w pamięci obrazu |
0050H |
Położenie kursora na każdej stronie (od 1-8) |
0060H |
Typ kursora |
0062H |
Bieżący numer wyświetlanej strony |
0063H |
Adres portu (rejestru indeksowego) układu 6845 sterownika wideo |
0065H |
Bieżący tryb pracy sterownika wideo |
0066H |
Numer wybranej palety (CGA) |
0067H |
Słowo zawierające adres ponownego wejścia w tryb rzeczywisty |
006BH |
Ostatnie nieoczekiwane przerwanie |
006CH |
Cztery bajty licznika zegara |
0070H |
Przepełnienie zegara (upłynęło 24 godz. od ostatniego odczytu zegara) |
0071H |
Flaga naciśnięcia klawiszy zatrzymania (bit 7) |
0072H |
Słowo wpisywane po resecie poprzez naciśnięcie CTRL-ALT-DEL |
0074H |
Stan sterownika dysku twardego |
0075H |
Liczba dysków twardych |
0076H |
Słowo zarezerwowane |
0078H |
Cztery bajty przeterminowania łączy szeregowych (LPT1-LPT4) |
007CH |
Cztery bajty przeterminowania łączy równoległych (COM1-COM4) |
0080H |
Wartość początku przemieszczenia bufora klawiatury |
0082H |
Wartość końca przemieszczenia klawiatury |
0084H |
Liczba wierszy (EGA) |
0085H |
Wartość znaku (EGA) |
0087H |
Bajt kontrolny (EGA) |
0088H |
Stan przełączników (EGA) |
0089H |
Bajt trybu kontrolnego (VGA) |
008AH |
Indeks tablicy kodu wyświetlania (VGA) |
008BH |
Identyfikator nośnika dysków miękkich |
008CH |
Stan kontrolera dysku twardego |
008DH |
Status błędu dysku twardego |
008EH |
Przerwanie kontrolne dysku twardego |
008FH |
Bajt informacyjny kontrolera dysków miękkich |
0090H |
Stan (nośnika) - dyskietki nr 0 |
0091H |
Stan (nośnika) - dyskietki nr 1 |
0092H |
Znacznik rozpoczęcia operacji na dyskietce 0 |
0093H |
Znacznik rozpoczęcia operacji na dyskietce 1 |
0094H |
Bieżący numer ścieżki dyskietki nr 0 |
0095H |
Bieżący numer ścieżki dyskietki nr 0 |
0096H |
Znacznik typu klawiatury |
0097H |
Znacznik diod LED na klawiaturze |
0098H |
Wskaźniki dotyczące zegara |
00A1H |
7 bajtów zarezerwowanych |
00A8H |
Wskaźnik do tablicy parametrów (EGA, VGA) |
00ACH |
34 bajty zarezerwowane |
00CEH |
Znacznik związany z bootowaniem systemu |
00D0H |
32 bajty zarezerwowane |
00F0H |
16 bajtów zarezerwowanych dla użytkownika |
0100H |
Status drukowania ekranu |
0101H |
13 bajtów zarezerwowanych |
010EH |
18 bajtów zarezerwowanych dla BASICa |
4.2. Pamięć górna
Pamięć górna UMA (Upper Memory Area) obejmuje obszar od adresu A0000h do FFFFFh, czyli uzupełnienie pamięci konwencjonalnej do 1Mb. W przestrzeni tej znajduje się pamięć wideo, obszary BIOSu, oraz bloki pamięci górnej. Zazwyczaj znajduje się tam także ramka pamięci EMS.
Alokację pamięci górnej możemy zrealizować na dwa sposoby, za pomocą funkcji XMS lub DOSu. W drugim przypadku alokacja wygląda identycznie jak w przypadku pamięci konwencjonalnej (funkcje 48H-4AH i 58H). Przydatne też mogą być dwie niżej opisane podfunkcje funkcji 58H. Pierwsza z nich określa czy pamięć górna wchodzi w skład obszarów przeznaczonych do alokacji. Nie oznacza to jednak, czy pamięć górna jest dostępna dla DOSu czy nie. Możemy się tego dowiedzieć przy pomocy drugiej funkcji. W tym celu trzeba nakazać podłączenie pamięci górnej i jeżeli wystąpi błąd o numerze 01 oznacza to że pamięć ta nie jest dostępna dla DOSu.
INT 21H, AX=5802H, Pobranie podłączenia pamięci górnej
Wejście:
Nie ma
Wyjście:
CF = 0
AL = 0 - pamięć górna nie podłączona
AL = 1 - pamięć górna podłączona
INT 21H, AX=5803H, Ustalenie podłączenia pamięci górnej
Wejście:
BX = 0 - odłączenie
BX = 1 - podłączenie
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
AX = kod błędu
W przypadku gdy pamięcią górną zarządzamy przez interfejs XMS, wykorzystujemy funkcje 10H i 11H. Więcej informacji o wywoływaniu funkcji XMS w kolejnym rozdziale.
AH=10H, Przydzielenie bloku pamięci górnej
Wejście:
DX = rozmiar bloku w paragrafach
Wyjście jeżeli AX=1:
BX = adres segmentowy bloku
DX = rozmiar bloku w paragrafach
Wyjście jeżeli AX=0:
BL = kod błędu
AH=11H, Zwolnienie bloku pamięci górnej
Wejście:
DX = adres segmentowy bloku
Wyjście jeżeli AX=1:
Nie ma
Wyjście jeżeli AX=0:
BL = kod błędu
4.2.1. Pamięć wideo
Pamięć wideo (video RAM), lub inaczej pamięć obrazu jest to obszar o wielkości dwóch segmentów począwszy od adresu A0000h który podpięty jest bezpośrednio do pamięci wewnętrznej karty. Rozwiązanie to jest bardzo korzystne, gdyż umożliwia swobodny zapis wprost do karty, szczególnie w trybach 256 kolorowych, gdzie organizacja pamięci jest spakowana i każdy bajt reprezentuje jeden piksel obrazu. Sytuacja komplikuje się w trybach o organizacji płatowej, gdyż konieczne jest dodatkowe programowanie karty przez porty.
Różne karty w różny sposób podpinają się pod dostępny obszar i tak karta CGA wykorzystuje przestrzeń począwszy od adresu B8000h, karty MDA i Hercules od B0000h, a karty EGA i VGA od A0000h. Nowoczesne karty SVGA, które posiadają dużą pamięć wewnętrzną, wykorzystują obszar video RAM jako ramkę która w danej chwili odzwierciedla tylko pewien fragment pamięci wewnętrznej. Programy pracujące w trybie chronionym mogą zamiennie wykorzystać linear framebuffer czyli ramkę umieszczoną gdziekolwiek w czterogigowej przestrzeni adresowej. Ramka ta jest ciągłym blokiem obrazującym całą pamięć wewnętrzną karty.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
4.2.2. Obszary BIOSu
Ponieważ pierwsze modele XT wyposażone były co najwyżej w 640kb pamięci, a procesor mógł zaadresować 1Mb, niewykorzystany obszar przestrzeni adresowej przeznaczono na BIOS różnych urządzeń. Znajduje się on w kościach pamięci ROM, która podpinana jest w przestrzeń adresową procesora. BIOS (Basic Input/Output System) jest podstawowym ustandaryzowanym systemem komunikacji z urządzeniami zewnętrznymi. W każdym komputerze istnieją przynajmniej dwa BIOSy: płyty głównej (umieszczony pod adresem 0F0000H, o rozmiarze 64kb), oraz karty graficznej (umieszczony pod adresem 0C0000H, o rozmiarze 32kb). Pozostałe BIOSy mogą istnieć w przypadku zainstalowanego sprzętu, który takowe posiada.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
--></><basefont face="Times New Roman" size=3>
4.2.3. Shadow RAM
Obszary BIOSu różnych urządzeń standardowo zapisane są w pamięci stałej ROM. Pamięć ta podpinana jest w odpowiednie miejsca przestrzeni adresowej, tak aby były one adresowalne z trybu rzeczywistego. Ponieważ obecnie w komputerach nie instaluje się pamięci poniżej 1Mb, korzystne stało się skopiowanie BIOSu urządzeń z pamięci ROM do RAM. Pamięć RAM jest o wiele szybsza, przez co zyskujemy szybszy dostęp do procedur BIOSu. Obszary, które mają być skopiowane ustalamy z poziomu konfiguracji BIOSu (przy starcie komputera). Pamięć RAM pełni więc jakby funkcję cienia pamięci ROM, stąd pochodzi nazwa - shadow RAM.
4.2.4. Bloki pamięci górnej
W pamięci górnej poza pamięcią wideo i obszarami BIOSu pozostają dziury, które nazwano blokami pamięci górnej, UMB (Upper Memory Block). Ponieważ bloków tych nie można posklejać, dlatego najlepiej nadają się one do przechowywania małych programów rezydentnych. Aby uzyskać dostęp do bloków UMB, w systemie musi być zainstalowany sterownik pamięci expanded (np. EMM386). Blokami pamięci górnej zarządzamy na dwa sposoby: wykorzystując interfejs XMS, lub poprzez standardowe funkcje DOSu. W drugim przypadku plik CONFIG.SYS musi zawierać linię DOS=UMB. Pozwala to nam także na ładowanie programów rezydentnych do pamięci górnej za pomocą poleceń LOADHIGH, INSTALLHIGH lub DEVICEHIGH.
4.3. Struktury DOSu
Aby system mógł sprawnie rozporządzać zasobami, stworzone zostały pewne struktury danych. Do istotnych dla nas zaliczamy listę list (jako strukturę nadrzędną zawierającą zmienne systemowe i wskaźniki do innych struktur), bloki kontroli pamięci MCB oraz blok wstępny programu PSP.
4.3.1. Lista list
Struktura listy list LL (List of List) zawiera część zmiennych systemowych oraz adresy innych istotnych struktur w DOSie. Wskaźnik do niej otrzymamy po wywołaniu funkcji 52H przerwania 21H. Struktura ta nie została nigdy do końca udokumentowana, a jej obecny wygląd ustalił się w wersji 4.0 systemu.
INT 21H, AH=52H, Pobranie adresu listy list
Wejście:
Nie ma
Wyjście:
ES:BX = adres listy list
Poniższa tabela zawiera opis pól listy list wraz z przemieszczeniem od adresu zwracanego przez funkcję 52H:
offset |
zawartość |
-000CH |
Licznik powtórzeń dzielonego dostępu |
-000AH |
Opóźnienie w dzielonym dostępie do plików |
-0008H |
Daleki wskaźnik do bieżącego bufora dyskowego |
-0004H |
Wskaźnik do bufora zawierającego nie odczytane znaki z konsoli |
-0002H |
Segment adresu nagłówka pierwszego bloku MCB |
0000H |
Daleki wskaźnik do pierwszego bloku parametrów dysku (DPB) |
0004H |
Daleki wskaźnik do systemowej tablicy plików (SFT) |
0008H |
Daleki wskaźnik do programu obsługi CLOCK$ |
000CH |
Daleki wskaźnik do programu obsługi CON |
0010H |
Maksymalna długość bloku (w bajtach) dla urządzeń blokowych |
0012H |
Daleki wskaźnik do informacji o buforach dyskowych |
0016H |
Daleki wskaźnik do tablicy katalogów roboczych |
001AH |
Daleki wskaźnik do systemowych tablic opisów plików |
001EH |
Liczba bloków FCB chronionych przed usunięciem |
0020H |
Liczba urządzeń blokowych |
0021H |
Wartość LASTDRIVE |
0022H |
Nagłówek programu obsługi urządzenia NULL |
0034H |
Liczba dysków dołączonych poleceniem JOIN |
0035H |
Wskaźnik w segmencie kodu systemu do listy specjalnych nazw programów |
0037H |
Daleki wskaźnik do procedury z usługami pomocniczymi |
003BH |
Daleki wskaźnik do łańcucha programów obsługi instalowanych systemów plików (IFS) |
003FH |
Wartość pierwszego parametru w poleceniu BUFFERS |
0041H |
Wartość drugiego parametru w poleceniu BUFFERS |
0043H |
Dysk z którego załadowano system |
0044H |
Zarezerwowane |
0046H |
Rozmiar pamięci rozszerzonej w kb |
4.3.2. Bloki kontroli pamięci
Bloki kontroli pamięci MCB (Memory Control Block) opisują obszary pamięci bazowej tak, aby ułatwić rozporządzanie nimi. Każdorazowo po wystąpieniu żądania przydziału, zwolnienia lub zmiany bloku pamięci system zmienia MCB tzn. wyodrębnia bloki o pożądanej wielkości z większego bloku, łączy sąsiednie nieużywane bloki lub modyfikuje ich zawartość. Segment pierwszego bloku uzyskamy odczytując pole -02H listy list. Segmenty kolejnych bloków uzyskujemy wg wzoru: SEGMENT_KOLEJNEGO_MCB = SEGMENT_BIEŻĄCEGO_MCB + ROZMIAR_BIEŻĄCEGO_MCB + 10H.
Blok MCB ma postać:
offset |
Opis |
0000H |
typ bloku: |
0001H |
Wskaźnik na PSP procesu zarządzającego danym blokiem (0 - blok bez właściciela) |
0003H |
Długość bloku w paragrafach |
0005H |
Zarezerwowane |
0008H |
Nazwa procesu który jest właścicielem bloku |
0010H |
Początek bloku pamięci |
4.3.3. Blok danych systemowych
Blok danych systemowych SD (System Data) jest pierwszym blokiem MCB (jego segment umieszczony jest w polu -02H listy list). Składa się on z podbloków tworzonych podczas interpretacji pliku CONFIG.SYS. Pojedynczy podblok wygląda następująco:
offset |
opis |
0000H |
typ podbloku: |
0001H |
Segment bloku opisywanego przez nagłówek |
0003H |
Długość podbloku w paragrafach |
0005H |
Zarezerwowane |
0008H |
Dla typu 'D' nazwa programu obsługi urządzenia |
4.3.4. Blok wstępny programu
Od momentu uruchomienia programu, system wykonuje kilka ważnych czynności. Przydziela programowi największy wolny blok pamięci, umieszcza na jego początku blok wstępny programu PSP (Program Segment Prefix) i ładuje program do pamięci. Rejestr segmentowy ES zawiera wartość równą początkowi bloku pamięci. Tak więc blok MCB znajduje się pod adresem ES:-0010H, blok PSP pod adresem ES:0000H natomiast sam program pod adresem ES:0100 (CS:0000H). Rola PSP polega na przekazaniu programowi w najprostszy sposób szeregu przydatnych informacji. Szczególne walory bloku wstępnego dostrzec można gdy dany program uruchamia podprogramy (może on wtedy dowolnie kreować PSP).
Adres bloku PSP możemy odczytywać i zapisywać za pomocą funkcji 50H, 51H i 62H przerwania 21H:
INT 21H, AH=50H, Ustawienie adresu bloku PSP
Wejście:
BX = adres segmentowy nowego bloku PSP
Wyjście:
CF = 0 - operacja powiodła się
CF = 1 - operacja nie powiodła się
INT 21H, AH=51H lub 62H, Pobranie adresu bloku PSP
Wejście:
Nie ma
Wyjście:
BX = adres segmentowy bieżącego bloku PSP
Poniższa tabela przedstawia zawartość bloku wstępnego programu:
offset |
zawartość |
0000H |
Kod rozkazu INT 20H (zakończenie programu) |
0002H |
Segment końca pamięci dostępnej dla programu |
0004H |
Zarezerwowane |
0005H |
Dalekie wywołanie do systemu DOS |
000AH |
Adres zakończenia programu |
000EH |
Adres obsługi CTRL-BREAK |
0012H |
Adres programu obsługi błędów krytycznych |
0016H |
Adres segmentowy bloku PSP procesu nadrzędnego |
0018H |
Tablica plików procesów (JFT) |
002CH |
Adres segmentowy otoczenia programu |
002EH |
Rejestr SS przechowywany podczas wywołania funkcji systemowych |
0030H |
Rejestr SP przechowywany podczas wywołania funkcji systemowych |
0032H |
Ilość elementów tablicy plików procesu (JFT) |
0034H |
Adres tablicy plików procesu (JFT) |
0038H |
Zarezerwowane |
0050H |
Kody rozkazów odwołania do funkcji INT 21H |
0053H |
Zarezerwowane |
005CH |
Standardowy blok opisu pliku (FCB) #1 |
006CH |
Standardowy blok opisu pliku (FCB) #2 |
0080H |
Bufor transmisji dyskowych (DTA) |
4.4. Ramka EMS
Ramka EMS jest blokiem o rozmiarze jednego segmentu (64kb) i jest wykorzystywana gdy w systemie występuje pamięć expanded. Pamięć ta podzielona jest na 16-kilobajtowe strony logiczne umieszczone w pamięci rozszerzonej. Dostęp do nich w trybie rzeczywistym jest realizowany właśnie przez ramkę, zawierającą cztery strony fizyczne do których możemy podczepić dowolne strony logiczne. Ramka może znajdować się gdziekolwiek w pamięci bazowej, w zależności od konfiguracji sterownika EMS. Więcej informacji o pamięci EMS w kolejnym rozdziale.
5. Pamięć rozszerzona
Pamięć rozszerzona już na tyle zadomowiła się w naszych komputerach, że prawdopodobnie nikt z nas nie ma do czynienia z maszyną w której by jej nie było. Dlatego większość obecnych systemów operacyjnych, pracujących w trybie chronionym nie ma problemów z zarządzaniem tą pamięcią, jednak taki system jak DOS, stworzony wyłącznie dla trybu rzeczywistego, nie "widzi" pamięci powyżej 1Mb. Dlatego też powstały liczne techniki alokacji i rozszerzenia udostępniające pamięć rozszerzoną. Programista staje więc przed wyborem schematu alokacji który mu najbardziej odpowiada, jednak nowoczesne programy powinny pracować w różnych konfiguracjach, dlatego dobrze by było gdyby dany program obsługiwał wszystkie dostępne schematy. Niniejszy rozdział ma za zadanie przybliżyć tą tematykę.
<><!--
body {font-family: Times New Roman}
p {font-family: Times New Roman}
td {font-family: Times New Roman}
pre {font-family: Courier}
--></><basefont face="Times New Roman" size=3>5.1. Linia adresowa A20
Tryb rzeczywisty stworzony na pierwsze komputery PC zakładał, że użytkownik ma do dyspozycji jedynie 1 Mb pamięci operacyjnej. Do obsłużenia takiej ilości pamięci wystarczało 20 lini adresowych procesora (numerowanych od A0 do A19). Gdy w komputerach zaczęto instalować więcej pamięci powstał problem niekompatybilności, gdyż standardowo po wywołaniu adresu odwołującego się do pamięci powyżej 1 Mb starsze procesory "zawijały" adres, który wskazywał na zupełnie inny obszar pamięci. Dla przykładu adres FFFF:0010 konwertowany był w rzeczywistości do adresu zerowego. Wynikało to z tego iż adres taki zajmował 21 bitów, z których najstarszy był wystawiany na nieistniejącą linię adresową. Dlatego w komputerach, których procesory posiadają już więcej nóżek adresowych linia A20 jest standardowo wyłączana, aby adres był "zawijany" tak jak w starszych modelach. Programy korzystające z pamięci rozszerzonej mogą ją włączać i wyłączać poprzez port sterujący klawiatury lub za pomocą interfejsu XMS. Drugi ze sposobów jest wyjątkowo prosty i polega na wywołaniu, w zależności od potrzeb, jednej z funkcji od 03h do 07h, pierwszy natomiast, wymaga programowania portu 064h. Programy korzystające z A20 powinny w pierwszej kolejności upewnić się czy zainstalowany jest sterownik XMS i jeżeli tak to włączać i wyłączać linię A20 przez jego funkcje.
Oto funkcje XMS sterujące stanem lini A20. Pamiętać zawsze należy aby po uprzednim odblokowaniu A20, przed zakończeniem programu ponownie ją zablokować. Istotny jest fakt, że sterownik XMS zlicza ilość odblokowań i zablokowań lini, tak więc jeżeli odblokujemy linię A20 np. dwukrotnie, a zablokujemy tylko raz, to w efekcie fizycznie pozostanie ona odblokowana. Funkcje 03H, 04H (przeznaczone dla programów obsługujących wyłącznie HMA) i 05H, 06H (dla programów mających dostęp do pamięci rozszerzonej) tworzą pary różniące się znacznie, dlatego np. przy odblokowaniu lini A20 funkcją 03H należy ją zablokować funkcją 04H.
AH=03H, Globalne odblokowanie lini A20
Wejście:
Nie ma
Wyjście jeżeli AX=1:
Nie ma
Wyjście jeżeli AX=0:
BL = kod błędu
AH=04H, Globalne zablokowanie lini A20
Wejście:
Nie ma
Wyjście jeżeli AX=1:
Nie ma
Wyjście jeżeli AX=0:
BL = kod błędu
AH=05H, Lokalne odblokowanie lini A20
Wejście:
Nie ma
Wyjście jeżeli AX=1:
Nie ma
Wyjście jeżeli AX=0:
BL = kod błędu
AH=06H, Lokalne zablokowanie lini A20
Wejście:
Nie ma
Wyjście jeżeli AX=1:
Nie ma
Wyjście jeżeli AX=0:
BL = kod błędu
AH=07H, Informacja o stanie fizycznym lini A20
Wejście:
Nie ma
Wyjście jeżeli BL=0:
AX = 1 - linia A20 odblokowana
AX = 0 - linia A20 zablokowana
Wyjście jeżeli BL<>0:
BL = kod błędu
Oto jak należy sterować stanem lini A20, gdy w systemie nie ma sterownika XMS:
.CODE
;odblokowanie lini A20
MOV AL,0D1H
OUT 64H,AL
CALL KLAW_GOT
MOV AL,0DFH
OUT 60H,AL
CALL KLAW_GOT
MOV CX,800H
PETLA:
CALL SPR_A20
JE SHORT OK
CALL DELAY
LOOP PETLA
;nie można odblokować A20
;w tym miejscu obsługa błędu
OK:
;linia A20 odblokowana
;zablokowanie lini A20
MOV AL,0D1H
MOV 64H,AL
CALL KLAW_GOT
MOV AL,0DDH
OUT 60h,AL
CALL KLAW_GOT
;linia A20 zablokowana
;procedura opóźniająca wykorzystująca
;zegar czasu rzeczywistego
PROC DELAY NEAR
CALL READ_ZEG
MOV AH,AL
PETLA1:
CALL READ_ZEG
CMP AL,AH
JE SHORT PETLA1
RET
ENDP DELAY
;sprawdzenie stanu zegara
PROC READ_ZEG NEAR
IN AL,40H
JMP SHORT $+2
JMP SHORT $+2
JMP SHORT $+2
IN AL,40H
RET
ENDP READ_ZEG
;sprawdzenie zawijania adresu powyżej 1Mb
PROC SPR_A20 NEAR
XOR AX,AX
MOV FS,AX
DEC AX
MOV GS,AX
CLI
MOV AL,[FS:0]
MOV AH,AL
NOT AL
XCHG [GS:10H],AL
CMP AH,[FS:0]
MOV [GS:10h],AL
STI
RET
ENDP SPR_A20
;sprawdzenie czy port danych klawiatury gotowy
PROC KLAW_GOT NEAR
MOV CX,800H
PETLA2:
CALL DELAY
IN AL,64H
TEST AL,2
LOOPNE PETLA2
RET
ENDP KLAW_GOT
--></><basefont face="Times New Roman" size=3>5.2. Pamięć wysoka
Pamięć wysoka HMA (High Memory Area) jest specyficznym obszarem, ponieważ należy on do pamięci rozszerzonej, lecz może być adresowany z trybu rzeczywistego. Wynika to z architektury samego procesora. Zauważmy że największy możliwy adres logiczny 0FFFFH:0FFFFH zapiszemy jako adres fizyczny 10FFEFH czyli leżący poza 1Mb. Tak więc w trybie rzeczywistym mamy dodatkowy blok pamięci o rozmiarze 65520 bajtów począwszy od adresu fizycznego 100000H. Blok ten obsługiwany jest przez interfejs XMS za pomocą funkcji 01H i 02H. W przypadku braku sterownika XMS, możemy sami pokusić się o zagospodarowanie HMA wykorzystując schemat alokacji BOTTOM-UP. Należy pamiętać że przy dostępie do HMA linia A20 musi być odblokowana. W przeciwnym wypadku zamazana zostanie tablica wektorów przerwań leżąca na początku pamięci bazowej, co nieuchronnie doprowadzi do zawieszenia komputera.
Korzystanie z pamięci wysokiej niesie ze sobą kilka ograniczeń. Adresy z jej obszaru nie powinny być wektorami przerwań. Nie powinny być również przekazywane do żadnej z funkcji DOSu. Program korzystający z obszaru HMA, powinien wykorzystać go maksymalnie, gdyż sterownik XMS udostępnia go tylko jednemu programowi. Stosuje on selekcję i nie przydziela HMA programowi który zażąda bloku o rozmiarze mniejszym niż jest określony parametrem HMAMIN.
Należy zauważyć że system DOS pomimo braku obsługi pamięci wysokiej może ją wykorzystać, zwalniając przy tym część pamięci bazowej. Obsługę HMA przez DOS realizujemy wpisując linię DOS=HIGH w pliku CONFIG.SYS.
Poniższe funkcje XMS sterują przydziałem pamięci HMA:
AH=01H, Alokacja obszaru HMA
Wejście:
DX = rozmiar wymaganego obszaru HMA
Wyjście jeżeli AX=1:
Nie ma
Wyjście jeżeli AX=0:
BL = kod błędu
AH=02H, Dealokacja obszaru HMA
Wejście:
Nie ma
Wyjście jeżeli AX=1:
Nie ma
Wyjście jeżeli AX=0:
BL = kod błędu
--></><basefont face="Times New Roman" size=3>
5.3. Pamięć EMS
Pamięć EMS (Expanded Memory Specification) zwana krótko pamięcią expanded, jest zagadnieniem z pogranicza pamięci bazowej i rozszerzonej. Polega ona na umieszczeniu stron logicznych (wielkości 16kb) w pamięci rozszerzonej, do których dostęp realizowany jest przez strony fizyczne tworzące ramkę EMS i znajdujące się w pamięci bazowej. Gdy zachodzi potrzeba, odpowiednie strony logiczne podczepiane są do stron fizycznych. Mechanizm ten implementowany jest na kilka sposobów: sprzętowo (poprzez kartę rozszerzającą lub sterownik na płycie) albo programowo (przez kopiowanie stron, lub wykorzystanie mechanizmu stronicowania pamięci). Do dziś przetrwała jedynie metoda wykorzystująca stronicowanie. Implementowana jest ona przez sterownik QEMM386, lub najpopularniejszy EMM386.
Obecnie przyjętym standardem dotyczącym pamięci expanded jest LIM EMS 4.0. Istotnymi zmianami w stosunku do starszych wersji jest wprowadzenie większej ilości stron fizycznych (umieszczonych poza ramką EMS), oraz zaimplementowanie interfejsu VCPI.
Funkcje sterownika EMS instalowane są pod przerwaniem 67H, nim jednak wywołamy jakąkolwiek z nich należy sprawdzić obecność sterownika w pamięci. Najprostsza metoda polega na sprawdzeniu czy wektor przerwania 67H wskazuje na jakąś procedurę obsługi (wektor pusty jest wypełniony zerami).
.CODE
;detekcja sterownika EMS przez kontrolę
;wektora INT 67H
XOR AX,AX
MOV ES,AX
MOV AX,[ES:67H*4]
MOV BX,[ES:67H*4+2]
OR AX,BX
JE SHORT NIE_MA
;sterownik EMS jest zainstalowany
NIE_MA:
;sterownik EMS nie jest zainstalowany
Metoda ta może jednak być zawodna, jeżeli np. jakiś nierozsądny program TSR podczepi się pod przerwanie 67H. Dlatego raczej należy używać metody sprawdzenia nazwy sterownika obsługującego to przerwanie. Nazwa sterownika EMS 'EMMXXXX0' umieszczona jest pod przemieszczeniem 0AH względem segmentu wektora INT 67H. Poniższy fragment kodu przedstawiający tą technikę dla ułatwienia wykorzystuje możliwości 32-bitowego adresowania procesorów 386.
.CODE
;detekcja sterownika EMS przez sprawdzenie nazwy
XOR AX,AX
MOV ES,AX
MOV ES,[ES:67H*4+2]
CMP [DWORD PTR ES:0AH],'XMME'
JNE SHORT NIE_MA
CMP [DWORD PTR ES:0AH+4],'0XXX'
JNE SHORT NIE_MA
;sterownik EMS jest zainstalowany
NIE_MA:
;sterownik EMS nie jest zainstalowany
Poniżej znajdują się najistotniejsze funkcje EMS których wywołanie realizuje się przez przerwanie 67H. Funkcje 41H-4EH obejmuje standard LIM EMS 3.x, natomiast funkcje 4FH-58H standard LIM EMS 4.0.
INT 67H, AH=40H, Pobranie statusu
Wejście:
Nie ma
Wyjście:
AH = 0 - mechanizmy EMS działają poprawnie
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=41H, Pobranie adresu ramki EMS
Wejście:
Nie ma
Wyjście jeżeli AH=0:
BX - adres segmentowy ramki EMS
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=42H, Pobranie liczby stron logicznych
Wejście:
Nie ma
Wyjście jeżeli AH=0:
BX - liczba niezaalokowanych stron logicznych
DX - liczba wszystkich stron logicznych
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=43H, Alokacja stron
Wejście:
BX - liczba stron do zaalokowania
Wyjście jeżeli AH=0:
DX - uchwyt zaalokowanych stron
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=44H, Podpięcie stron
Wejście:
AL - numer strony fizycznej w którą zostanie wpięta logiczna
BX - numer wpinanej strony logicznej
DX - uchwyt zaalokowanych stron
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=45H, Dealokacja stron
Wejście:
DX - uchwyt stron
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=46H, Pobranie wersji LIM EMS
Wejście:
Nie ma
Wyjście jeżeli AH=0:
AL - numer wersji standardu LIM EMS
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=47H, Zapamiętanie stanu odwzorowania stron
Wejście:
DX - uchwyt stron zaalokowanych przez program
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=48H, Odtworzenie stanu odwzorowania stron
Wejście:
DX - uchwyt stron zaalokowanych przez program
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=4BH, Pobranie liczby uchwytów
Wejście:
Nie ma
Wyjście jeżeli AH=0:
DX - liczba wszystkich otwartych uchwytów
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=4CH, Pobranie liczby stron związanych z uchwytem
Wejście:
DX - uchwyt stron
Wyjście jeżeli AH=0:
BX - liczba stron
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=4DH, Pobranie liczby stron związanych z wszystkimi uchwytami
Wejście:
ES:DI - wskaźnik do obszaru danych
Wyjście jeżeli AH=0:
BX - liczba otwartych uchwytów
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Wskaźnik w ES:DI powinien wskazywać obszar o wielkości wyznaczonej przez liczbę otwartych uchwytów (funkcja 4BH) pomnożoną przez cztery. Zapisana w nim informacja ma postać poniższych struktur:
STRUC UCHW_INFO
;wartość uchwytu
UCHW DW ?
;liczba stron związanych z uchwytem
PAGES DW ?
ENDS UCHW_INFO
INT 67H, AX=4E00H, Pobranie stanu odwzorowania stron
Wejście:
ES:DI - wskaźnik obszaru do zapisania stanu
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=4E01H, Ustalenie stanu odwzorowania stron
Wejście:
DS:SI - wskaźnik obszaru do pobrania stanu
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=4E02H, Pobranie i ustalenie stanu odwzorowania stron
Wejście:
ES:DI - wskaźnik obszaru do zapisania stanu
DS:SI - wskaźnik obszaru do pobrania stanu
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=4E03H, Pobranie rozmiaru obszaru do zapisu danych o stanie odwzorowania stron
Wejście:
Nie ma
Wyjście jeżeli AH=0:
AL = rozmiar obszaru w bajtach
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! W podfunkcjach 00H-02H funkcji 4EH wskaźnik w ES:DI powinien wskazywać obszar w którym zapisane zostaną dane dotyczące odwzorowania, natomiast wskaźnik w DS:SI obszar z którego zostaną pobrane dane. Wielkość obszaru pobieramy podfunkcją 03H
INT 67H, AX=4F00H, Pobranie częściowego stanu odwzorowania stron
Wejście:
DS:SI - wskaźnik obszaru danych o wybranych stronach
ES:DI - wskaźnik obszaru do zapisania stanu
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=4F01H, Ustalenie częściowego stanu odwzorowania stron
Wejście:
DS:SI - wskaźnik obszaru do pobrania stanu
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=4F02H, Pobranie rozmiaru obszaru do zapisu danych o częściowym stanie odwzorowania stron
Wejście:
BX = liczba wybranych stron
Wyjście jeżeli AH=0:
AL = rozmiar obszaru w bajtach
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Funkcja 4FH działa analogicznie do funkcji 4EH, jednak informacja dotyczy tylko wybranych stron. W podfunkcji 00H wskaźnik w DS:SI wskazuje obszar w którym zapisana jest informacja których stron fizycznych dotyczy zapisanie stanu.
INT 67H, AH=50H, Podpięcie wielu stron
Wejście:
AL = 0 - funkcja bazuje na numerach stron fizycznych
AL = 1 - funkcja bazuje na segmentach stron fizycznych
DX - uchwyt stron
CX - ilość elementów w obszarze danych
DS:SI - wskaźnik obszaru do pobrania stanu
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Elementy w tablicy wskazywanej przez ES:DI mają postać poniższych struktur:
STRUC PODP_STRON
;numer strony logicznej
LOG DW ?
;numer strony fizycznej (dla AL=0)
;lub jej adres segmentowy (dla AL=1)
FIZ DW ?
ENDS PODP_STRON
INT 67H, AH=51H, Realokacja stron
Wejście:
DX - uchwyt stron
BX - wymagana liczba stron
Wyjście jeżeli AH=0:
BX - wynikowa liczba zaalokowanych stron
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=5200H, Pobranie atrybutu uchwytu
Wejście:
DX - uchwyt
Wyjście jeżeli AH=0:
AL = 0 - uchwyt ulotny
AL = 1 - uchwyt nieulotny
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=5201H, Ustalenie atrybutu uchwytu
Wejście:
DX - uchwyt
BL = 0 - uchwyt ulotny
BL = 1 - uchwyt nieulotny
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=5202H, Określenie ulotności pamięci
Wejście:
Nie ma
Wyjście jeżeli AH=0:
AL = 0 - pamięć ulotna
AL = 1 - pamięć nieulotna
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Funkcja 52H ma znaczenie wyłącznie w sprzętowych implementacjach pamięci EMS. Atrybut uchwytu decyduje wówczas, czy pamięć zostanie wymazana czy nie po restarcie systemu kombinacją CTRL-ALT-DEL.
INT 67H, AX=5300H, Pobranie nazwy uchwytu
Wejście:
DX - uchwyt
ES:DI - wskaźnik obszaru do zapisania nazwy
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=5301H, Ustalenie nazwy uchwytu
Wejście:
DX - uchwyt
DS:SI - wskaźnik obszaru z zapisaną nazwą
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Nazwa uchwytu złożona jest z maksymalnie 8 znaków. Puste pola wypełniane są wartością 00H.
INT 67H, AX=5400H, Pobranie nazw wszystkich uchwytu
Wejście:
ES:DI - wskaźnik obszaru do zapisania nazw
Wyjście jeżeli AH=0:
AL = liczba otwartych uchwytów
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Elementy w tablicy wskazywanej przez ES:DI mają postać poniższych struktur:
STRUC NAZW_STRON
;wartość uchwytu
UCHW DW ?
;nazwa uchwytu
NAZWA DB 8 DUP(?)
ENDS NAZW_STRON
INT 67H, AX=5401H, Wyszukanie uchwytu przez nazwę
Wejście:
DS:SI - wskaźnik obszaru z zapisaną nazwą
Wyjście jeżeli AH=0:
DX - uchwyt
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=5402H, Pobranie maksymalnej liczby uchwytów
Wejście:
Nie ma
Wyjście jeżeli AH=0:
BX - liczba dostępnych uchwytów
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AH=57H, Skopiowanie lub wymiana obszaru pamięci
Wejście:
AL = 0 - skopiowanie obszaru
AL = 1 - wymiana obszaru
DS:SI - wskaźnik struktury opisującej obszary
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Gdy AL=0 dane są kopiowane z obszaru źródłowego do docelowego, natomiast gdy AL=1 dane są wymieniane pomiędzy tymi obszarami. Struktura opisująca ma postać:
STRUC KOP_WYM
;liczba bajtów do skopiowania
WIELK DD ?
;rodzaj obszaru źródłowego
;0 - dla pamięci bazowej
;1 - dla stron EMS
ZR_RODZ DB ?
;uchwyt stron EMS obszaru źródłowego
;(dla pamięci bazowej = 0)
ZR_UCHW DW ?
;przemieszczenie względem segmentu
;lub strony EMS obszaru źródłowego
ZR_OFFS DW ?
;segment pamięci bazowej
;lub numer strony EMS obszaru źródłowego
ZR_SEG DW ?
;poniższe parametry obszaru docelowego są
;analogiczne do obszaru źródłowego
DC_RODZ DB ?
DC_UCHW DW ?
DC_OFFS DW ?
DC_SEG DW ?
ENDS KOP_WYM
INT 67H, AX=5800H, Pobranie adresów segmentowych stron fizycznych
Wejście:
ES:DI - wskaźnik obszaru do zapisania danych
Wyjście jeżeli AH=0:
CX - liczba stron fizycznych
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=5801H, Pobranie liczby stron fizycznych
Wejście:
Nie ma
Wyjście jeżeli AH=0:
CX - liczba stron fizycznych
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Wskaźnik w ES:DI podfunkcji 00H powinien wskazywać obszar o wielkości wyznaczonej przez liczbę stron fizycznych pomnożoną przez cztery. Zapisana w nim informacja ma postać poniższych struktur:
STRUC SEG_FIZ
;segment strony fizycznej
SEG DW ?
;numer strony fizycznej
NUM DW ?
ENDS SEG_FIZ
--></><basefont face="Times New Roman" size=3>
5.4. Schematy alokacji pamięci rozszerzonej
Od czasu powstania pamięci rozszerzonej powstało wiele standardów według których programy mogły zarządzać dodatkową pamięcią. Niektóre z nich stanowią rozbudowane programy nie tylko ułatwiające dostęp do pamięci, ale także zawierające interfejsy realizujące podstawowe czynności związane z przełączaniem do trybu chronionego. Niestety każdy ze sposobów alokacji pamięci wiąże się z odmienną konfiguracją systemu co staje się ciężarem dla użytkownika, który musi za każdym razem restartować komputer modyfikując przy tym plik CONFIG.SYS. Dlatego programista powinien zadbać o to, aby jego program uruchamiał się z dowolnej konfiguracji. Może do tego wykorzystać jeden z dostępnych DOS extenderów, lub stworzyć własny algorytm do zarządzania pamięcią.
Złożone sterowniki pamięci zazwyczaj wykorzystują inne, prymitywniejsze sterowniki i schematy: XMS wykorzystuje schemat TOP-DOWN, VCPI(EMS) wykorzystuje XMS, natomiast DPMI wszystkie podrzędne. Dlatego program obsługujący pamięć powinien w pierwszej kolejności wykorzystywać schematy najbardziej rozbudowane. Poniżej przedstawiony jest sposób postępowania po wykryciu istnienia danego schematu:
DPMI - po jego wykryciu program powinien przełączyć się w tryb chroniony za pomocą jego interfejsu, bez dodatkowego sprawdzania innych schematów,
VCPI - program może sprawdzić istnienie pamięci TOP-DOWN i BOTTOM-UP i dołączyć ją do puli zarządzanych obszarów, następnie zaalokować pamięć XMS i na końcu VCPI. Ten sposób postępowania znacznie przyspiesza sam proces alokacji gdyż VCPI przydziela po jednej stronie 4kb, natomiast XMS dowolnie duży obszar. Przełączenie do trybu chronionego musi być wykonane przez interfejs VCPI,
XMS - jeżeli nie wykryty został sterownik VCPI program alokuje pamięć za pomocą interfejsu XMS, opcjonalnie alokując także pamięć TOP-DOWN i BOTTOM-UP. Przełączenie do trybu chronionego następuje przez samodzielnie stworzony do tego mechanizm,
TOP-DOWN - program alokuje pamięć według tego schematu licząc się jednocześnie ze schematem BOTTOM-UP. Przełączenie do trybu chronionego następuje przez samodzielnie stworzony do tego mechanizm,
BOTTOM-UP - zalecane jest wykorzystanie zamiennie schematu TOP-DOWN który oferuje te same możliwości i jest bardziej elegancki.
--></><basefont face="Times New Roman" size=3>
5.4.1. BOTTOM-UP
Schemat BOTTOM-UP zwany także schematem VDISK, jest dosyć przestarzałym sposobem na alokację pamięci i raczej nie należy go używać. Zamiast niego programy mogą zastosować schemat TOP-DOWN. Różnica polega na tym że BOTTOM-UP alokuje pamięć rozszerzoną poczynając od adresu 100000H (1Mb) w górę, natomiast TOP-DOWN od górnej granicy w dół. Niezbędne jest jednak poznanie mechanizmu BOTTOM-UP, gdyż jeżeli jest obecny wyznacza on dolną granicę dla TOP-DOWN.
W praktyce uzyskanie górnej granicy BOTTOM-UP polega na odczytaniu wartości w dwóch miejscach pamięci, porównaniu ich i wybraniu wyższej. Wynika to z różnych implementacji tego schematu. Pierwsze z miejsc wskazywane jest przez wektor przerwania 19H. Pod przemieszczeniem 12H względem segmentu tego wektora znajduje się sygnatura 'VDISK V', pod przemieszczeniem 2CH mniej znaczące słowo a pod 2EH bardziej znaczący bajt górnej granicy. Drugie miejsce znajduje się w bootblocku umieszczonym na początku pamięci rozszerzonej (wymaga to odblokowania lini A20). Przemieszczenie 03H względem 1Mb wskazuje sygnaturę 'VDISK', natomiast przemieszczenie 1EH słowo górnej granicy wyrażone w kilobajtach. Poniższy kod pokazuje w jaki sposób otrzymać górną granicę schematu BOTTOM-UP:
.CODE
;odczytanie górnej granicy BOTTOM-UP
XOR AX,AX
MOV FS,AX
DEC AX
MOV GS,AX
MOV EDX,0100000H
MOV EBX,EDX
;sprawdzenie pierwszej lokacji
MOV ES,[FS:19H*4+2]
CMP [DWORD PTR ES:12H],'SIDV'
JNE SHORT NAST
MOV DL,[ES:2EH]
SHL EDX,16
MOV DX,[ES:2CH]
;sprawdzenie drugiej lokacji
NAST: CMP [DWORD PTR GS:13H],'SIDV'
JNE SHORT POROWN
XOR EBX,EBX
MOV BX,[GS:2EH]
SHL EBX,10
;porównanie wyników
POROWN: CMP EDX,EBX
JB SHORT KONIEC
MOV EBX,EDX
KONIEC:
;w EBX zwracana jest górna granica BOTTOM-UP
;w przypadku nie istnienia tego schematu
;wartość EBX wynosi 100000H (1Mb)
--></><basefont face="Times New Roman" size=3>
5.4.2. TOP-DOWN
Schemat TOP-DOWN (zwany także schematem INT 15) jest stosunkowo prosty i zalecane jest jego używanie zamiast schematu BOTTOM-UP. Bazuje on na funkcji 88H przerwania 15H. Funkcja ta zwraca rozmiar pamięci rozszerzonej wyrażonej w kilobajtach:
INT 15H, AH=88H, Odczytanie rozmiaru pamięci rozszerzonej
Wejście:
Nie ma
Wyjście jeżeli CF=0:
AX - rozmiar pamięci w kilobajtach
Wyjście jeżeli CF=1:
Nie ma
Sposób alokacji pamięci polega na stworzeniu procedury obsługi przerwania 15H która w przypadku wystąpienia funkcji 88H zwraca wielkość pamięci rozszerzonej pomniejszonej o obszar zaalokowany przez siebie. Przy alokowaniu pamięci tą metodą programy muszą liczyć się ze schematem BOTTOM-UP, który wyznacza dolną granicę pamięci (w przypadku jego braku granica ta wynosi 1Mb).
.CODE
;wielkość pamięci do zaalokowania (w kb)
;w naszym przypadku 3Mb
ILE_KB EQU 3072
;na wejściu rejestr BX powinien zawierać
;dolną granicę pamięci
XOR EAX,EAX
MOV AH,88H
INT 15H
SHL EAX,10
ADD EAX,100000H
XOR ECX,ECX
MOV CX,ILE_KB
SHL ECX,10
SUB EAX,ECX
CMP EAX,EBX
JAE SHORT OK
;błąd - za mało pamięci
OK:
;instalacja obsługi INT 15H
XOR SI,SI
MOV ES,SI
MOV SI,CS
MOV DI,OFFSET INT_15H
CLI
XCHG [ES:15H*4],DI
XCHG [ES:15H*4+2],SI
MOV [CS:ADRES],DI
MOV [CS:ADRES+2],SI
STI
;procedura alokacji zakończona
;EAX - początek zaalokowanej pamięci
;ECX - rozmiar zaalokowanej pamięci
;procedura obsługi przerwania
PROC INT_15H FAR
MOV [CS:AH_REJ],AH
PUSHF
CALL [DWORD PTR CS:ADRES]
CMP [BYTE PTR CS:AH_REJ],88H
JNE SHORT EX_INT
SUB AX,ILE_KB
EX_INT: IRET
ENDP INT_15H
;zmienna przechowująca rejestr AH
AH_REJ DB ?
;adres starej procedury obsługi INT 15H
ADRES DW ?,?
5.4.3. XMS
XMS (eXtended Memory Specification) jest interfejsem udostępnianym przez program XMM (eXtended Memory Manager). Przekazuje on programom do dyspozycji funkcje związane z kontrolą lini A20, alokacją pamięci górnej, wysokiej i rozszerzonej. Prostota i wygoda obsługi XMS sprawiła że jest to najpopularniejszy standard dotyczący zarządzania pamięcią komputerów PC. Nawet system Windows95 do pracy wymaga zainstalowanego sterownika XMS. Najbardziej znanym jest program HIMEM dołączany do systemów DOS i Windows95.
Pamięć XMS jest ciągłym blokiem w którym wyodrębnia się bloki EMB (Extended Memory Block). Należy jednak pamiętać że XMS nie korzysta z mechanizmu stronicowania i nie potrafi sklejać wolnych bloków. Oznacza to że jeżeli program A zaalokuje obszar o rozmiarze AA, następnie program B obszar BB wtedy największy wolny blok jest równy WIELKOŚĆ_PAMIĘCI - AA - BB. Jeżeli z kolei program A zwolni swój blok, to największy wolny EMB dalej będziemy wyznaczać tym wzorem, aż do zwolnienia pamięci przez program B. Dlatego jeżeli dany program wymaga maksimum pamięci, a jest ona podzielona, powinien on wykorzystać stronicowanie pamięci w celu zachowania jej ciągłości.
Przed wywołaniem funkcji XMS należy sprawdzić jego obecność i pobrać adres wejścia. Zajmują się tym funkcje przerwania 2FH przedstawione poniżej. Przy każdym wywołaniu funkcji XMS należy w AH umieścić jej numer a następnie wykonać daleki skok pod adres zwrócony w parze ES:BX.
INT 2FH, AX=4300H, Detekcja XMS
Wejście:
Nie ma
Wyjście:
AL = 80H - sterownik XMS zainstalowany
INT 2FH, AX=4310H, Pobranie adresu wejścia do procedur XMS
Wejście:
Nie ma
Wyjście:
ES:BX = adres wejścia do procedur XMS
Poniższy kod pokazuje sposób detekcji XMS i przykładowe wywołanie funkcji 00H.
.DATA
;pobrany adres wejścia do procedur XMS
ADR_XMS DW ?,?
.CODE
;detekcja sterownika XMS
MOV AX,4300H
INT 2FH
CMP AL,80H
JE SHORT OK
;błąd - sterownik XMS nie zainstalowany
OK:
;pobranie adresu wejścia
MOV AX,4310H
INT 2FH
MOV [ADR_XMS],BX
MOV [ADR_XMS+2],ES
;wywołanie funkcji 00H
MOV AH,00H
CALL [DWORD PTR ADR_XMS]
Większość funkcji XMS została przedstawiona w rozdziałach związanych tematycznie. Tutaj zajmiemy się głównie funkcjami dotyczącymi pamięci rozszerzonej. Funkcje 88H-8FH wykorzystują możliwości 32-bitowego adresowania procesorów 386 i wyższych.
AH=00H, Pobranie numeru wersji
Wejście:
Nie ma
Wyjście:
AX = numer wersji XMS
BX = wewnętrzny numer programu XMM
DX = 1 - istnieje pamięć wysoka
DX = 0 - nie istnieje pamięć wysoka
AH=08H, Informacja o wolnej pamięci rozszerzonej
Wejście:
Nie ma
Wyjście jeżeli BL=0:
AX = rozmiar największego wolnego bloku (w kb)
DX = sumaryczny rozmiar wszystkich bloków (w kb)
Wyjście jeżeli BL<>0:
BL = kod błędu
AH=09H, Przydzielenie bloku pamięci
Wejście:
DX = żądany rozmiar bloku (w kb)
Wyjście jeżeli AL=1:
DX = uchwyt przydzielonego bloku
Wyjście jeżeli AL=0:
BL = kod błędu
AH=0AH, Zwolnienie bloku pamięci
Wejście:
DX = uchwyt bloku
Wyjście jeżeli AL=1:
Nie ma
Wyjście jeżeli AL=0:
BL = kod błędu
AH=0BH, Skopiowanie bloku danych
Wejście:
DS:SI = adres obszaru z informacją
Wyjście jeżeli AL=1:
Nie ma
Wyjście jeżeli AL=0:
BL = kod błędu
Uwaga! Wskaźnik w DS:SI powinien wskazywać obszar w którym zapisana jest informacja o bloku źródłowym i docelowym. Struktura ta ma postać:
STRUC KOP_INFO
;rozmiar kopiowanego bloku w bajtach
ROZM DD ?
;uchwyt bloku źródłowego lub 00H jeżeli
;obszar znajduje się w pamięci bazowej
SR_UCHW DW ?
;przemieszczenie w bloku źródłowym, lub
;adres logiczny (segment:offset) dla
;pamięci bazowej
SR_OFFS DD ?
;parametry analogiczne dla bloku docelowego
DC_UCHW DW ?
DC_OFFS DD ?
ENDS KOP_INFO
AH=0CH, Zablokowanie bloku pamięci
Wejście:
DX = uchwyt bloku
Wyjście jeżeli AL=1:
DX:BX = 32-bitowy adres liniowy zablokowanego bloku
Wyjście jeżeli AL=0:
BL = kod błędu
AH=0DH, Odblokowanie bloku pamięci
Wejście:
DX = uchwyt bloku
Wyjście jeżeli AL=1:
Nie ma
Wyjście jeżeli AL=0:
BL = kod błędu
AH=0EH, Pobranie informacji o bloku pamięci
Wejście:
DX = uchwyt bloku
Wyjście jeżeli AL=1:
BH = ilość zablokowań danego bloku
BL = ilość wolnych uchwytów pamięci
DX = rozmiar bloku (w kb)
Wyjście jeżeli AL=0:
BL = kod błędu
AH=0FH, Zmiana rozmiaru bloku pamięci
Wejście:
DX = uchwyt bloku
BX = nowy rozmiar bloku (w kb)
Wyjście jeżeli AL=1:
Nie ma
Wyjście jeżeli AL=0:
BL = kod błędu
AH=88H, Informacja o wolnej pamięci rozszerzonej
Wejście:
Nie ma
Wyjście jeżeli BL=0:
EAX = rozmiar największego wolnego bloku (w kb)
EDX = sumaryczny rozmiar wszystkich bloków (w kb)
ECX = najwyższy fizyczny adres pamięci
Wyjście jeżeli BL<>0:
BL = kod błędu
AH=89H, Przydzielenie bloku pamięci
Wejście:
EDX = żądany rozmiar bloku (w kb)
Wyjście jeżeli AL=1:
DX = uchwyt przydzielonego bloku
Wyjście jeżeli AL=0:
BL = kod błędu
AH=8EH, Pobranie informacji o bloku pamięci
Wejście:
DX = uchwyt bloku
Wyjście jeżeli AL=1:
BH = ilość zablokowań danego bloku
CX = ilość wolnych uchwytów pamięci
EDX = rozmiar bloku (w kb)
Wyjście jeżeli AL=0:
BL = kod błędu
AH=8FH, Zmiana rozmiaru bloku pamięci
Wejście:
DX = uchwyt bloku
EBX = nowy rozmiar bloku (w kb)
Wyjście jeżeli AL=1:
Nie ma
Wyjście jeżeli AL=0:
BL = kod błędu
5.4.4. VCPI
Interfejs VCPI (Virtual Control Program Interface) powstał jako rozszerzenie standardu EMS począwszy od wersji LIM EMS 4.0. Udostępnia on możliwość alokacji pamięci rozszerzonej i mechanizmy przełączania do trybu chronionego dla programów pracujących w tym trybie. Ponieważ w przypadku zainstalowanego sterownika EMS system pracuje w trybie V86 (Virtual 8086 machine) na najniższym poziomie uprzywilejowania, program jest zmuszony do wykorzystania VCPI do przełączenia do trybu chronionego, gdyż inaczej spowoduje on powstanie wyjątku np. przy dostępie do rejestrów sterujących.
VCPI ma dosyć ograniczone możliwości alokowania pamięci. Każdym wywołaniem funkcji alokującej przydzielana jest nam tylko jedna strona o wielkości 4kb. Dlatego programy wymagające dużego obszaru pamięci powinny ze względu na szybkość alokować bloki za pomocą XMS, zablokowywać je i na podstawie otrzymanego adresu dokonywać podziału na 4kb strony. Pamiętać należy że adres każdej strony powinien być podzielny przez 4kb, zgodnie z mechanizmem stronicowania.
Aby sprawdzić obecność VCPI należy najpierw zdetektować EMS, następnie zaalokować jedną jego stronę (aby upewnić się że mechanizm VCPI jest włączony) a następnie wywołać funkcję DEH przerwania 67H. Ze względu na fakt iż mechanizm VCPI występuje tylko na maszynach 386 i wyższych można przed całą procedurą dokonać sprawdzenia typu procesora. Oto funkcja DEH:
INT 67H, AX=DE00H, Detekcja VCPI
Wejście:
Nie ma
Wyjście jeżeli AH=0:
BX = numer wersji VCPI
Wyjście jeżeli AH<>0:
AH = kod błędu
Funkcje VCPI dostępne są pod przerwaniem 67H. Pełne ich zrozumienie wymaga znajomości architektury procesorów 386 w górę a w szczególności trybu chronionego i mechanizmu stronicowania pamięci. Dzielą się one na funkcje dostępne z poziomu trybu V86 i trybu chronionego. Oto pierwsza grupa:
INT 67H, AX=DE01H, Pobranie interfejsu trybu chronionego
Wejście:
ES:DI = wskaźnik do tablicy stron
DS:SI = wskaźnik do trzech deskryptorów
Wyjście jeżeli AH=0:
DI = przemieszczenie w tablicy stron
EBX = przemieszczenie w segmencie kodu
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Wskaźnik w ES:DI powinien wskazywać na pierwszą tablicę stron (opisującą pierwsze 4Mb pamięci) która zostanie wypełniona przez VCPI. W rejestrze DI zwracane jest przemieszczenie w tej tablicy wskazujące pierwszy nie używany element (od tego miejsca tablica może być wypełniana przez nasz program). Wskaźnik w DS:SI wskazuje na pole w którym umieszczone zostaną trzy deskryptory należące do VCPI. Pierwszy z nich opisuje segment kodu VCPI i wraz z przemieszczeniem zwracanym w EBX powinien być używany jako adres wejścia do procedur VCPI dla trybu chronionego.
INT 67H, AX=DE02H, Pobranie adresu najwyższej strony
Wejście:
Nie ma
Wyjście jeżeli AH=0:
EDX = fizyczny adres strony
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=DE03H, Pobranie ilości wolnych stron
Wejście:
Nie ma
Wyjście jeżeli AH=0:
EDX = ilość wolnych stron
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=DE04H, Alokacja strony
Wejście:
Nie ma
Wyjście jeżeli AH=0:
EDX = fizyczny adres strony
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=DE05H, Dealokacja strony
Wejście:
EDX = fizyczny adres strony
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=DE06H, Pobierz adres fizyczny strony umieszczonej w pamięci bazowej
Wejście:
CX = numer strony
Wyjście jeżeli AH=0:
EDX = fizyczny adres strony
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Numer strony (rejestr CX) jest adresem liniowym podzielonym przez 4kb.
INT 67H, AX=DE07H, Odczytanie CR0
Wejście:
Nie ma
Wyjście jeżeli AH=0:
EBX = wartość rejestru CR0
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=DE08H, Odczytanie rejestrów uruchomieniowych
Wejście:
ES:DI = wskaźnik do zapisu rejestrów
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! W obszarze ES:DI zapisane zostaną 32-bitowe rejestry uruchomieniowe (DR0-DR7). Pomimo braku rejestrów DR4 i DR5 dla nich również zostanie przydzielone miejsce.
INT 67H, AX=DE09H, Zapisanie rejestrów uruchomieniowych
Wejście:
ES:DI = wskaźnik z zapisanymi rejestrami
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Z obszaru ES:DI odczytane zostaną 32-bitowe wartości i wpisane do rejestrów uruchomieniowych (DR0-DR7). Pola dotyczące rejestrów DR4 i DR5 będą ignorowane.
INT 67H, AX=DE0AH, Odczytanie wektorów przerwań sprzętowych
Wejście:
Nie ma
Wyjście jeżeli AH=0:
BX = wektor układu PIC master (IRQ0-IRQ7)
CX = wektor układu PIC slave (IRQ8-IRQ15)
Wyjście jeżeli AH<>0:
AH = kod błędu
INT 67H, AX=DE0BH, Ustawienie wektorów przerwań sprzętowych
Wejście:
BX = wektor układu master (IRQ0-IRQ7)
CX = wektor układu slave (IRQ8-IRQ15)
Wyjście jeżeli AH=0:
Nie ma
Wyjście jeżeli AH<>0:
AH = kod błędu
Uwaga! Wektor oznacza numer przerwania programowego dla pierwszego przerwania sprzętowego danego układu. Ustawienie wektorów oznacza jedynie poinformowanie VCPI o ich zmianie. Program sam musi dokonać zmiany programując układ 8259A. Podczas tej operacji oraz podczas przekazywania wektorów do VCPI przerwania muszą być wyłączone. Program powinien odtworzyć poprzednie ustawienia wektorów przed zakończeniem działania.
INT 67H, AX=DE0CH, Przełączenie do trybu chronionego
Wejście:
ESI = adres struktury w pierwszym megabajcie
Wyjście:
Nie ma
Uwaga! Rejestr ESI powinien zawierać liniowy adres struktury w pierwszym megabajcie pamięci zawierającej wartości wpisywane do rejestrów procesora. Przerwania podczas całej operacji przełączenia powinny być wyłączone. Funkcja ładuje rejestry CR3, GDTR, IDTR, LDTR i TR po czym wykonuje skok pod zadany adres. Po otrzymaniu kontroli program powinien wypełnić własnymi wartościami rejestry stosu (SS:ESP) i włączyć przerwania. Oto postać struktury:
STRUC WART_REJ
;wartość rejestru CR3
CR3_REG DD ?
;liniowy adres w pierwszym megabajcie zawierający
;6-bajtową wartość ładowaną do GDTR
GDTR_AD DD ?
;liniowy adres w pierwszym megabajcie zawierający
;6-bajtową wartość ładowaną do IDTR
IDTR_AD DD ?
;selektor ładowany do LDTR
LDTR_SL DW ?
;selektor ładowany do TR
TR_SL DW ?
;adres pod który wykonany zostanie skok
;po przełączeniu, wielkość ta zależy od
;typu segmentu:
;USE16 - 4 bajty (DWORD)
;USE32 - 6 bajtów (FWORD)
ADRES DF ?
ENDS WART_REJ
Druga grupa funkcji VCPI dostępna jest z poziomu trybu chronionego. Wywołanie każdej funkcji polega na wykonaniu dalekiego skoku pod adres wejścia VCPI. Selektor umieszczany w rejestrze segmentowym wyznaczamy sami. Jest to selektor pierwszego deskryptora VCPI (adres trzech deskryptorów przekazujemy funkcją DE01H). Przemieszczenie natomiast zwracane jest w rejestrze EBX po wykonaniu funkcji DE01H. Pierwsze trzy funkcje trybu chronionego są analogiczne dla trybu V86, omówienia wymaga jedynie funkcja DE0CH.
AX=DE03H, Pobranie ilości wolnych stron
AX=DE04H, Alokacja strony
AX=DE05H, Dealokacja strony
AX=DE0CH, Przełączenie do trybu V86
Wejście:
SS:ESP = szczyt stosu z odłożonymi wartościami
DS = selektor segmentu pierwszego megabajtu
Wyjście:
Nie ma
Uwaga! Rejestr DS powinien zawierać selektor segmentu obejmującego początkowy obszar pamięci, którego adres bazowy wynosi zero a limit jest określony przy wywołaniu funkcji DE01H (rejestr DI). Na szczyt stosu (znajdującego się w pierwszym megabajcie) odłożone być powinny kolejno następujące wartości charakterystyczne dla trybu V86 (wszystkie wartości są 32-bitowe mimo iż uwzględniane jest tylko ich mniej znaczące słowo):
(DWORD) wartość rejestru GS
(DWORD) wartość rejestru FS
(DWORD) wartość rejestru DS
(DWORD) wartość rejestru ES
(DWORD) wartość rejestru SS
(DWORD) wartość rejestru SP
(DWORD) zarezerwowane dla EFLAGS (ignorowane)
(DWORD) wartość rejestru CS
(DWORD) wartość rejestru IP
Podczas przełączenia do trybu V86 przerwania powinny być wyłączone. Przed wykonaniem procedury program powinien także wyzerować bit TS w rejestrze CR0. W przeciwnym wypadku operacje na liczbach zmiennoprzecinkowych spowodują wyjątek 07H.
5.4.5. DPMI
Interfejs DPMI udostępnia zintegrowane środowisko trybu chronionego programom pracującym pod kontrolą DOSu. Może on zaistnieć w postaci zwykłego dosowego sterownika pamięci, lub jako wirtualne środowisko DOSu zaimplementowane w zupełnie odrębnym systemie operacyjnym. Między innymi właśnie dzięki DPMI programy wykorzystujące ten interfejs mogą działać praktycznie bezbłędnie pod systemem Windows95.
DPMI udostępnia funkcje zarządzania pamięcią bazową i rozszerzoną, deskryptorami oraz przerwaniami i wyjątkami. W prosty sposób umożliwia on także uruchamianie kodu trybu rzeczywistego i standardowych przerwań BIOSu i DOSu. Program pracujący w środowisku DPMI nie może liczyć na to, że będzie uruchomiony na najwyższym poziomie uprzywilejowania, dlatego też zmuszony jest do używania funkcji tego interfejsu (jako jedynego sposobu na modyfikację rejestrów, których zmiana na niższym poziomie uprzywilejowania generuje wyjątek).
Prawie wszystkie funkcje DPMI dostępne są pod przerwaniem 31H . Wywołanie ich może nastąpić jedynie z poziomu trybu chronionego. Dlatego w trybie rzeczywistym, po wykryciu DPMI program musi przełączyć się do trybu chronionego (za pomocą jego interfejsu) i dopiero wtedy wykonywać wszelkie operacje związane z alokacją pamięci itd. Obecność DPMI ustalamy za pomocą funkcji 1687H przerwania 2FH. Zwraca ona szereg danych charakteryzujących środowisko, m.in. numer wersji, implementację 16-bitową lub 32-bitową, oraz adres procedury przełączającej do trybu chronionego.
INT 2FH, AX=1687H, Pobranie adresu procedury przełączającej do trybu chronionego
Wejście:
Nie ma
Wyjście jeżeli AX=0:
BX = flagi
CL = typ procesora
CL = 02H - 80286
CL = 03H - 80386
CL = 04H - 80486
DX = numer wersji DPMI
SI = wielkość obszaru dla DPMI (w paragrafach)
ES:DI = adres procedury przełączającej
Wyjście jeżeli AX<>0:
Nie ma
Uwaga! Rejestr BX zawiera tylko jedną flagę (bit 0). Jeżeli jest ona ustawiona oznacza to 32-bitową implementację DPMI, w przeciwnym wypadku 16-bitową. Rejestr SI zawiera rozmiar pamięci bazowej przeznaczonej dla DPMI, która zaalokowana musi być przez klienta.
Po wywołaniu powyższej funkcji program powinien przełączyć się do trybu chronionego, wykonując daleki skok pod adres zwracany przez parę ES:DI. Oto funkcja realizująca przełączenie:
Przełączenie do trybu chronionego
Wejście:
AX = flagi
ES = adres obszaru dla DPMI
Wyjście jeżeli CF=0:
CS = selektor kodu trybu rzeczywistego
SS = selektor stosu trybu rzeczywistego
DS = selektor danych trybu rzeczywistego
ES = selektor PSP klienta
FS, GS = 0
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Rejestr AX powinien zawierać tylko jedną flagę (bit 0). Jeżeli jest ona ustawiona oznacza to program 32-bitowy. Rejestr ES jest segmentem obszaru pamięci bazowej przeznaczonej dla DPMI, którego rozmiar podaje funkcja 1687H. Wszystkie zwracane selektory wskazują segmenty 16-bitowe. Ich adres bazowy wyznaczany jest na podstawie zawartości rejestrów segmentowych przed wywołaniem funkcji, natomiast limit wynosi 0FFFFH (z wyjątkiem segmentu PSP - 100H). Po prawidłowym wykonaniu funkcji program wykonywany jest w trybie chronionym.
Programy pracujące na przemian w dwóch trybach, chronionym i rzeczywistym (lub V86) mają do dyspozycji funkcję 1686H, która podaje aktualny tryb pracy procesora. Dużo szybszym sposobem jest jednak użycie instrukcji SMSW i sprawdzenie bitu 0 słowa stanu procesora (instrukcja SMSW nie powoduje wyjątku na niższym poziomie uprzywilejowania).
INT 2FH, AX=1686H, Pobranie trybu pracy procesora
Wejście:
Nie ma
Wyjście:
AX = 0 - praca w trybie chronionym
AX <> 0 - praca w trybie rzeczywistym (lub V86)
A oto funkcje DPMI (zgodne ze standardem DPMI 0.9) dostępne wyłącznie z poziomu trybu chronionego:
INT 21H, AX=4CH, Zakończenie działania programu
Wejście:
AL = kod wyjścia
Wyjście:
Nie ma
Uwaga! Pomimo że funkcja ta przypomina standardową funkcję DOSu, powinna być wywołana z trybu chronionego przez program pracujący w tym trybie. Wywołanie tej funkcji z trybu rzeczywistego może spowodować w konsekwencji zawieszenie systemu.
INT 31H, AX=0000H, Zaalokowanie deskryptorów w tablicy LDT
Wejście:
CX = ilość deskryptorów do zaalokowania
Wyjście jeżeli CF=0:
AX = podstawowy selektor
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Zwracany selektor jest pierwszym z szeregu. Aby uzyskać kolejne selektory należy dodać wartość zwracaną przez funkcję 0003H.
INT 31H, AX=0001H, Zwolnienie deskryptora tablicy LDT
Wejście:
BX = selektor deskryptora
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0002H, Konwersja segmentu trybu rzeczywistego do deskryptora
Wejście:
BX = segment trybu rzeczywistego
Wyjście jeżeli CF=0:
AX = selektor deskryptora
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Powstały deskryptor nie może być modyfikowany ani zwalniany. Limit każdego deskryptora wynosi 0FFFFH.
INT 31H, AX=0003H, Pobranie wartości wyznaczającej kolejne selektory
Wejście:
Nie ma
Wyjście jeżeli CF=0:
AX = wartość dodawana do selektora
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0006H, Pobranie adresu bazowego deskryptora
Wejście:
BX = selektor deskryptora
Wyjście jeżeli CF=0:
CX:DX = 32-bitowy adres liniowy
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0007H, Ustawienie adresu bazowego deskryptora
Wejście:
BX = selektor deskryptora
CX:DX = 32-bitowy adres liniowy
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0008H, Ustawienie limitu deskryptora
Wejście:
BX = selektor deskryptora
CX:DX = 32-bitowy limit (rozmiar)
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0009H, Ustawienie praw dostępu deskryptora
Wejście:
BX = selektor deskryptora
CL = podstawowy bajt flag deskryptora
CH = rozszerzony bajt flag (tylko 386)
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=000AH, Stworzenie analogicznego segmentu kodu
Wejście:
BX = selektor segmentu kodu
Wyjście jeżeli CF=0:
AX = selektor segmentu danych
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Funkcja tworzy segment danych na podstawie podanego segmentu kodu. Ponieważ segment kodu może być tylko wykonywany lub odczytywany, umożliwia to ingerencję w kod programu.
INT 31H, AX=000BH, Pobranie deskryptora
Wejście:
BX = selektor deskryptora
ES:EDI = wskaźnik obszaru do zapisu deskryptora
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Wskaźnik w ES:EDI powinien wskazywać obszar o wielkości 8 bajtów, gdzie skopiowany zostanie deskryptor.
INT 31H, AX=000CH, Ustawienie deskryptora
Wejście:
BX = selektor deskryptora
ES:EDI = wskaźnik obszaru z deskryptorem
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Wskaźnik w ES:EDI powinien wskazywać obszar o wielkości 8 bajtów, gdzie zapisany jest deskryptor.
INT 31H, AX=000DH, Alokacja specyficznego deskryptora
Wejście:
BX = selektor deskryptora
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Specyficznymi deskryptorami nazywamy pierwsze 16 deskryptorów tablicy LDT. DPMI rezerwuje je specjalnie na potrzeby tej funkcji. Mogą one być niedostępne w przypadku gdy inny program dokonał ich alokacji. Specyficzne deskryptory zwalniamy funkcją 0001H.
INT 31H, AX=0100H, Alokacja bloku pamięci bazowej
Wejście:
BX = rozmiar bloku (w paragrafach)
Wyjście jeżeli CF=0:
AX = segment bloku trybu rzeczywistego
DX = selektor bloku
Wyjście jeżeli CF=1:
AX = kod błędu (zwracany przez DOS)
BX = rozmiar największego wolnego bloku
Uwaga! Jeżeli żądany rozmiar jest większy niż 64kb zaalokowane zostanie więcej deskryptorów, które wyznaczamy za pomocą funkcji 0003H. W 32-bitowych implementacjach limit pierwszego deskryptora będzie równy rozmiarowi bloku, limity pozostałych deskryptorów będą równe 0FFFFH, oprócz ostatniego, którego limit będzie resztą z dzielenia rozmiaru bloku przez 64kb. W 16-bitowych implementacjach limit pierwszego deskryptora będzie także równy 0FFFFH. Program nie powinien zwalniać lub modyfikować otrzymanych deskryptorów.
INT 31H, AX=0101H, Zwolnienie bloku pamięci bazowej
Wejście:
DX = selektor bloku
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
AX = kod błędu (zwracany przez DOS)
Uwaga! Wraz z blokiem pamięci zwalniane są przydzielone mu deskryptory.
INT 31H, AX=0102H, Zmiana rozmiaru bloku pamięci bazowej
Wejście:
BX = nowy rozmiar bloku (w paragrafach)
DX = selektor bloku
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
AX = kod błędu (zwracany przez DOS)
BX = rozmiar największego wolnego bloku
Uwaga! Zmiana rozmiaru bloku dodaje, zwalnia, lub modyfikuje deskryptory. Jeżeli wymagana jest alokacja deskryptora, a kolejny deskryptor w LDT jest zajęty funkcja zwróci błąd.
INT 31H, AX=0200H, Pobranie wektora przerwania trybu rzeczywistego
Wejście:
BL = numer przerwania
Wyjście:
CF = 0
CX:DX = wektor przerwania (SEGMENT:OFFSET)
INT 31H, AX=0201H, Ustawienie wektora przerwania trybu rzeczywistego
Wejście:
BL = numer przerwania
CX:DX = wektor przerwania (SEGMENT:OFFSET)
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0202H, Pobranie wektora wyjątku
Wejście:
BL = numer wyjątku (00H-1FH)
Wyjście jeżeli CF=0:
CX:EDX = wektor wyjątku (SELEKTOR:OFFSET)
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0203H, Ustawienie wektora wyjątku
Wejście:
BL = numer wyjątku (00H-1FH)
CX:EDX = wektor wyjątku (SELEKTOR:OFFSET)
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Nie wszystkie wyjątki są udostępniane przez DPMI. Procedura obsługi wyjątku powinna zakończyć działanie wykonując instrukcję dalekiego powrotu lub skok do następnej procedury obsługi. Przerwania są wyłączone podczas trwania wyjątku.
INT 31H, AX=0204H, Pobranie wektora przerwania
Wejście:
BL = numer przerwania
Wyjście:
CF = 0
CX:EDX = wektor przerwania (SELEKTOR:OFFSET)
INT 31H, AX=0205H, Ustawienie wektora przerwania
Wejście:
BL = numer przerwania
CX:EDX = wektor przerwania (SELEKTOR:OFFSET)
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0300H, Symulacja przerwania trybu rzeczywistego
Wejście:
BL = numer przerwania
BH = flagi
CX = ilość słów na stosie
ES:EDI = struktura zawierająca rejestry
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0301H, Wywołanie procedury trybu rzeczywistego zakończonej dalekim powrotem
Wejście:
BH = flagi
CX = ilość słów na stosie
ES:EDI = struktura zawierająca rejestry
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0302H, Wywołanie procedury trybu rzeczywistego zakończonej IRET
Wejście:
BH = flagi
CX = ilość słów na stosie
ES:EDI = struktura zawierająca rejestry
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Funkcje 0300H-0302H służą do wykonywania kodu trybu rzeczywistego. Rejestr BH zawiera flagę (bit 0) która ustawiona oznacza przywrócenie standardowych ustawień kontrolera przerwań dla tego trybu (ignorowane w trybie V86). Rejestr CX zawiera liczbę słów do skopiowania ze stosu trybu chronionego do stosu trybu rzeczywistego. Adres w ES:EDI wskazuje na strukturę zawierającą wartości rejestrów przekazywane do trybu rzeczywistego. Po powrocie z funkcji struktura ta zawiera zmodyfikowane przez nią wartości.
STRUC WART_REJ
;rejestr EDI
EDI_REG DD ?
;rejestr ESI
ESI_REG DD ?
;rejestr EBP
EBP_REG DD ?
;zarezerwowane
ZAREZ DD ?
;rejestr EBX
EBX_REG DD ?
;rejestr EDX
EDX_REG DD ?
;rejestr ECX
ECX_REG DD ?
;rejestr EAX
EAX_REG DD ?
;rejestr FLAGS
FLAGS DD ?
;rejestr ES
ES_REG DW ?
;rejestr DS
DS_REG DW ?
;rejestr FS
FS_REG DW ?
;rejestr GS
GS_REG DW ?
;rejestr IP
IP_REG DW ?
;rejestr CS
CS_REG DW ?
;rejestr SP
SP_REG DW ?
;rejestr SS
SS_REG DW ?
ENDS WART_REJ
INT 31H, AX=0303H, Alokacja adresu call-back
Wejście:
DS:ESI = adres procedury trybu chronionego
ES:EDI = struktura zawierająca rejestry
Wyjście jeżeli CF=0:
CX:DX = adres trybu rzeczywistego
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Adresy call-back służą do przekazywania kontroli procedurom trybu chronionego z poziomu trybu rzeczywistego. Rejestry DS:ESI zawierają adres procedury która zostanie wywołana. ES:EDI jest wskaźnikiem do struktury w której przekazane zostaną rejestry. Para CX:DX zwraca adres trybu rzeczywistego który może być np. wektorem przerwania 21H. Wtedy po każdym wystąpieniu tego przerwania kontrol zostanie przekazana do procedury trybu chronionego.
INT 31H, AX=0304H, Zwolnienie adresu call-back
Wejście:
CX:DX = adres trybu rzeczywistego
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0305H, Pobranie adresów zapisu/odczytu stanu
Wejście:
Nie ma
Wyjście jeżeli CF=0:
AX = rozmiar bufora zapisu stanu
BX:CX = adres dla trybu rzeczywistego
SI:EDI = adres dla trybu chronionego
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Funkcja zwraca adresy procedur dla trybu rzeczywistego i chronionego które służą do zapisania stanu rejestrów w odrębnym trybie (np. funkcja wywołana w trybie rzeczywistym zapisuje stan dla trybu chronionego) zanim przełączenie między trybami spowoduje ich zmianę. Nie jest wymagane zapisanie stanu przy użyciu funkcji 0300H-0302H, może być jednak użyteczne przy "surowym" przełączeniu.
Zapisanie/odczytanie stanu
Wejście:
ES:EDI = bufor zapisu stanu
AL = 0 zapisanie stanu
AL = 1 odczytanie stanu
INT 31H, AX=0306H, Pobranie adresów "surowego" przełączania między trybami
Wejście:
Nie ma
Wyjście jeżeli CF=0:
BX:CX = adres dla trybu rzeczywistego
SI:EDI = adres dla trybu chronionego
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Funkcja pobiera adresy procedur przełączających między trybami. Przełączenie w ten sposób nie zapewnia zapisania stanu i żadnych dodatkowych operacji, lecz jest proporcjonalnie szybkie.
"Surowe" przełączenie między trybami
Wejście:
AX = nowy DS
CX = nowy ES
DX = nowy SS
EBX = nowy ESP
SI = nowy CS
EDI = nowy EIP
INT 31H, AX=0400H, Pobranie wersji DPMI
Wejście:
Nie ma
Wyjście:
CF = 0
AX = wersja DPMI
BX = flagi
CL = typ procesora
CL = 02H - 80286
CL = 03H - 80386
CL = 04H - 80486
DH = wektor układu master kontrolera przerwań sprzętowych
DL = wektor układu slave kontrolera przerwań sprzętowych
Uwaga! Rejestr BX zawiera następujące flagi:
bit 0 - ustawiony dla 32-bitowych implementacji
bit 1 - ustawiony dla wirtualnego środowiska DOSu
bit 2 - ustawiony jeżeli zaimplementowana jest pamięć wirtualna
bit 3 - zarezerwowany
Reszta bitów jest wyzerowana i zarezerwowana dla późniejszych implementacji.
INT 31H, AX=0500H, Pobranie informacji o wolnej pamięci
Wejście:
ES:EDI = wskaźnik do zapisu informacji
Wyjście:
CF = 0
Uwaga! Wskaźnik w ES:EDI powinien wskazywać 48-bajtowy obszar w której zapisana zostanie struktura przedstawiona poniżej. Jeżeli pamięć wirtualna nie jest zaimplementowana istotna jest tylko pierwsza wartość, reszta równa jest -1 (0FFFFFFFFH).
STRUC WOLNA_PAM
;długość największego wolnego bloku
NAJ_BLO DD ?
;maksymalna liczba możliwych stron
;do zaalokowania
MAX_NZB DD ?
;maksymalna liczba możliwych stron
;do zaalokowania i zablokowania
MAX_ZB DD ?
;liniowa przestrzeń w stronach
LIN_STR DD ?
;liczba niezablokowanych stron
NZB_STR DD ?
;liczba wolnych stron
WOL_STR DD ?
;liczba wszystkich stron
WSZ_STR DD ?
;wolna liniowa przestrzeń w stronach
WL_STR DD ?
;rozmiar pliku lub partycji wymiany stron
WYMIAN DD ?
;zarezerwowane
ZAREZ DD 3 DUP(?)
ENDS WOLNA_PAM
INT 31H, AX=0501H, Alokacja bloku pamięci
Wejście:
BX:CX = rozmiar bloku w bajtach
Wyjście jeżeli CF=0:
BX:CX = liniowy adres bloku
SI:DI = uchwyt bloku
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0502H, Zwolnienie bloku pamięci
Wejście:
SI:DI = uchwyt bloku
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0503H, Zmiana rozmiaru bloku pamięci
Wejście:
BX:CX = nowy rozmiar bloku w bajtach
SI:DI = uchwyt bloku
Wyjście jeżeli CF=0:
BX:CX = nowy liniowy adres bloku
SI:DI = nowy uchwyt bloku
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0600H, Zablokowanie obszaru pamięci
Wejście:
BX:CX = początkowy adres liniowy obszaru
SI:DI = rozmiar obszaru
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Zablokowanie obszaru powoduje że nie może on być zapisany na dysk. W szczególności blokowane powinny być obszary zawierające procedury obsługi wyjątków, przerwań i ich stosów. DPMI przechowuje licznik blokowań obszaru, więc aby go odblokować trzeba to zrobić tyle razy ile nastąpiło zablokowanie. Funkcje dotyczące blokowania/odblokowania obszaru są istotne tylko w wypadku, gdy zaimplementowana jest pamięć wirtualna.
INT 31H, AX=0601H, Odblokowanie obszaru pamięci
Wejście:
BX:CX = początkowy adres liniowy obszaru
SI:DI = rozmiar obszaru
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0602H, Zaznaczenie obszaru pamięci bazowej jako podlegającego wymianie
Wejście:
BX:CX = początkowy adres liniowy obszaru
SI:DI = rozmiar obszaru
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Funkcja wyznacza, które obszary pamięci bazowej mogą podlegać wymianie (standardowo cała pamięć bazowa jest zablokowana).
INT 31H, AX=0603H, Zaznaczenie obszaru pamięci bazowej jako nie podlegającego wymianie
Wejście:
BX:CX = początkowy adres liniowy obszaru
SI:DI = rozmiar obszaru
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0604H, Pobranie rozmiaru strony fizycznej
Wejście:
Nie ma
Wyjście jeżeli CF=0:
BX:CX = rozmiar strony
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0702H, Ustawienie stron do wymiany w pierwszej kolejności
Wejście:
BX:CX = początkowy adres liniowy stron
SI:DI = ilość bajtów do zaznaczenia
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0703H, Usunięcie zawartości stron
Wejście:
BX:CX = początkowy adres liniowy stron
SI:DI = ilość bajtów do usunięcia
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0800H, Odwzorowanie adresu fizycznego
Wejście:
BX:CX = adres fizyczny obszaru pamięci
SI:DI = wielkość obszaru
Wyjście jeżeli CF=0:
BX:CX = adres logiczny obszaru pamięci
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Funkcja powinna być używana tylko w przypadku konieczności odwzorowania adresu fizycznego zwracanego np. przez jakieś urządzenie (kartę graficzną, sieciową) na adres liniowy. Jeżeli pamięć wirtualna nie jest zaimplementowana adres fizyczny jest równoznaczny z adresem liniowym.
INT 31H, AX=0900H, Pobranie statusu i zablokowanie przerwań wirtualnych
Wejście:
Nie ma
Wyjście:
CF = 0
AL = 0 jeżeli przerwania były zablokowane
AL = 1 jeżeli przerwania były odblokowane
INT 31H, AX=0901H, Pobranie statusu i odblokowanie przerwań wirtualnych
Wejście:
Nie ma
Wyjście:
CF = 0
AL = 0 jeżeli przerwania były zablokowane
AL = 1 jeżeli przerwania były odblokowane
INT 31H, AX=0902H, Pobranie statusu przerwań wirtualnych
Wejście:
Nie ma
Wyjście:
CF = 0
AL = 0 jeżeli przerwania były zablokowane
AL = 1 jeżeli przerwania były odblokowane
Uwaga! Niektóre implementacje DPMI nie zezwalają na zablokowanie przerwań, w zamian przechowując status przerwań wirtualnych który pobieramy i zmieniamy funkcjami 0900H-0902H. Program może wykorzystać też instrukcje CLI i STI do zmiany statusu, lecz nie może go wyznaczać na podstawie rejestru FLAGS.
INT 31H, AX=0A00H, Pobranie adresu specyficznych funkcji
Wejście:
DS:ESI = wskaźnik sygnatury
Wyjście jeżeli CF=0:
ES:EDI = adres specyficznych funkcji
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Funkcja pobiera adres funkcji specyficznych dla danej implementacji DPMI. Funkcje te muszą być udokumentowane przez interfejs udostępniający je. Sygnatura służy rozpoznaniu konkretnego interfejsu. Składa się ona z znaków ASCII zakończonych znakiem NULL (00H). Funkcja ta w szczególnych przypadkach może modyfikować wszystkie podstawowe rejestry.
INT 31H, AX=0B00H, Ustawienie pułapki
Wejście:
BX:CX = adres liniowy pułapki
DL = rozmiar pułapki (1, 2 lub 4)
DH = typ pułapki
DH = 0 - wykonanie
DH = 1 - zapisanie
DH = 2 - odczytanie/zapisanie
Wyjście jeżeli CF=0:
BX = uchwyt pułapki
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Funkcja wykorzystuje możliwości procesorów 386 i wyższych kontrolowania kodu i danych programu (rejestry uruchomieniowe DR0-DR7). Jeżeli pułapka jest typu "wykonywanie" jej rozmiar powinien wynosić 1.
INT 31H, AX=0B01H, Zwolnienie pułapki
Wejście:
BX = uchwyt pułapki
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
INT 31H, AX=0B02H, Pobranie statusu pułapki
Wejście:
BX = uchwyt pułapki
Wyjście jeżeli CF=0:
AX = flagi
Wyjście jeżeli CF=1:
Nie ma
Uwaga! Rejestr AX zawiera jedną flagę (bit 0). Jeżeli jest ona ustawiona oznacza to że pułapka została osiągnięta.
INT 31H, AX=0B03H, Wyzerowanie statusu pułapki
Wejście:
BX = uchwyt pułapki
Wyjście jeżeli CF=0:
Nie ma
Wyjście jeżeli CF=1:
Nie ma
5.4.6. Alternatywny sposób alokacji
Większość sterowników pamięci rozszerzonej udostępnia nam cały dostępny obszar, ograniczony jedynie rozmiarem fizycznie zainstalowanej pamięci. Co jednak zrobić, gdy jesteśmy autorami systemu operacyjnego, który jest w pełni niezależną od DOSu i jego sterowników platformą? Jak wiadomo BIOS zwraca nam rozmiar pamięci rozszerzonej, jednak maksymalna wartość jaką może nam przekazać to 64Mb, a obecnie "mocne" komputery (np. serwery) wyposażone są w dużo większą pamięć operacyjną. W takim przypadku należy stworzyć własną procedurę sprawdzającą rozmiar zainstalowanej pamięci.
Aby dokonać sprawdzenia pamięci trzeba przełączyć procesor w tryb chroniony i stworzyć segment obejmujący całą dostępną czterogigową przestrzeń adresową (adres bazowy równy zero, limit równy 4Gb). Pamiętać należy o następujących warunkach które muszą być spełnione:
- mechanizm stronicowania pamięci musi być wyłączony,
- przerwania muszą być zablokowane,
- linia A20 musi być odblokowana,
- procedura testująca musi znajdować się w pierwszym megabajcie (ten obszar nie poddajemy testowi ze względu na obecność BIOSu),
- wyłączone muszą być mechanizmy urządzeń które podczepiają pamięć wewnętrzną (np liniowa ramka pamięci wideo).
Samo działanie procedury sprowadza się do przetestowania obecności jednego bajta z każdej kolejnej strony pamięci (o wielkości 4kb) poczynając od adresu 100000H (ominięcie pierwszego megabajta). Jeżeli wystąpi błąd, oznacza to że adres nie jest dostępny. Funkcję należy zakończyć po wykryciu pierwszego błędu. Oto prosty i skuteczny algorytm testowania pamięci:
.CODE
;przyjmujemy że rejestr DS wskazuje segment
;obejmujący całą przestrzeń 4Gb
MOV ESI,100000H-1000H
CLI
NAST:
ADD ESI,1000H
OR ESI,ESI
JE SHORT KONIEC
MOV AL,[ESI]
XOR AL,0FFH
MOV AH,AL
MOV [ESI],AL
MOV AL,[ESI]
XOR [BYTE PTR ESI],0FFH
CMP AL,AH
;jeżeli komórka w porządku skok
JE SHORT NAST
;komórka nie istnieje
;wyznaczenie górnego adresu
KONIEC:
STI
DEC ESI
;ESI = adres fizyczny najwyższej komórki
5.5. DOS extendery
Określeniem DOS extender nazywamy program, który spełnia trzy podstawowe funkcje:
- ładuje klienta i wykonuje podstawowe czynności inicjalizacyjne,
- udostępnia klientowi środowisko trybu chronionego,
- udostępnia klientowi pamięć rozszerzoną.
Zazwyczaj klientem extendera jest jeden program, na którego potrzeby ustawiane są rejestry i tablice systemowe. Extender nie jest częścią systemu DOS, dlatego każdy program powinien zapewniać obecność extendera, którego jest klientem. Dobrą metodą jest doklejanie kodu klienta do pliku extendera, lecz istnieją też extendery które nie udostępniają takiej możliwości. Do najpopularniejszych extenderów zaliczają się: DOS4GW, PMODE/W, WDOSX, czy DOS32.
Interfejs jaki oferuje DOS extender zależy od jego twórców. Może to być zestaw dowolnych funkcji, najczęściej jednak funkcje te zgodne są ze standardem DPMI. Płynie z tego wiele korzyści. Po pierwsze DPMI posiada duży wachlarz funkcji, w zupełności wystarczający na potrzeby extendera. Po drugie nie trzeba tworzyć mechanizmów tłumaczenia funkcji extendera na funkcje DMPI i po trzecie nie trzeba pisać dokładnej dokumentacji, gdyż DPMI taką już posiada. Każdy extender, który oferuje dodatkowe funkcje może skorzystać z mechanizmu jaki oferuje funkcja 0A00H przerwania 31H.
Pamiętać należy, że aby extender był dobry, powinien pracować w każdej konfiguracji systemu, dlatego też dobrze jest jeżeli obsługuje on wszystkie udokumentowane schematy alokacji pamięci rozszerzonej.
5.6. Funkcje BIOSu
System BIOS oferuje trzy dosyć prymitywne funkcje związane z pamięcią rozszerzoną. Ich użyteczność (poza funkcją 88H używaną w schemacie TOP-DOWN) jest raczej wątpliwa.
INT 15H, AH=87H, Skopiowanie bloku pamięci rozszerzonej
Wejście:
CX = ilość słów do skopiowania (maksymalnie 8000H)
ES:SI = adres tablicy GDT
Wyjście jeżeli CF=0:
AH = 0
Wyjście jeżeli CF=1:
AH = kod błędu
Uwaga! Podczas działania funkcji przerwania są wyłączone. Globalna tablica deskryptorów powinna wyglądać jak poniżej. Bajt praw dostępu deskryptorów powinien być równy 93H.
00H - deskryptor zarezerwowany
08H - deskryptor zarezerwowany
10H - deskryptor obszaru źródłowego
18H - deskryptor obszaru docelowego
20H - deskryptor zarezerwowany
28H - deskryptor zarezerwowany
INT 15H, AH=88H, Odczytanie rozmiaru pamięci rozszerzonej
Wejście:
Nie ma
Wyjście jeżeli CF=0:
AX - rozmiar pamięci w kilobajtach
Wyjście jeżeli CF=1:
Nie ma
INT 15H, AH=89H, Przełączenie do trybu chronionego
Wejście:
BH = wektor układu PIC master (IRQ0-IRQ7)
BL = wektor układu PIC slave (IRQ8-IRQ15)
ES:SI = adres tablicy GDT
Wyjście jeżeli CF=0:
AH = 0
Wyjście jeżeli CF=1:
AH = 0FFH
Uwaga! Rejestr BX zawiera wektory kontrolera przerwań sprzętowych. Globalna tablica deskryptorów powinna zawierać przynajmniej 8 deskryptorów w kolejności przedstawionej poniżej (ostatni deskryptor jest inicjowany przez BIOS):
00H - deskryptor pusty
08H - deskryptor GDT
10H - deskryptor IDT
18H - deskryptor segmentu danych (DS)
20H - deskryptor segmentu danych (ES)
28H - deskryptor segmentu stosu (SS)
30H - deskryptor segmentu kodu (CS)
38H - deskryptor BIOSu
5.7. Instalacja sterowników
Poniżej znajduje się krótki opis instalacji sterownika XMM (eXtended Memory Manager) implementującego pamięć XMS oraz EMM (Expanded Memory Manager) implementującego pamięć EMS. Opis dotyczy standardowych sterowników rozpowszechnianych wraz z systemem DOS.
XMM
Sterownik XMM znajduje się w pliku HIMEM.SYS. Aby go zainstalować, należy umieścić stosowną linię w pliku CONFIG.SYS. Jej postać ukazana jest poniżej (w nawiasach kwadratowych podane są opcjonalne parametry):
DEVICE=[dysk:][ścieżka]HIMEM.SYS [/HMAMIN=n] [/NUMHANDLES=n] [/INT15=n] [/MACHINE=n] [/A20CONTROL:ON|OFF] [/EISA] [/SHADOWRAM:ON|OFF] [/VERBOSE]
Poniżej znajduje się opis wszystkich parametrów sterownika XMM:
/HMAMIN=n - parametr n (podawany w kilobajtach) określa minimalny rozmiar pamięci wysokiej. Program który żąda przydziału tej pamięci musi podać rozmiar wykorzystywanego przez siebie obszaru. W przypadku gdy rozmiar ten będzie mniejszy niż wartość parametru HMAMIN pamięć HMA nie zostanie przydzielona. Domyślą wartością jest 0, co oznacza że pamięć przydzielona zostanie pierwszemu programowi który jej zażąda.
/NUMHANDLES=n - parametr n jest liczbą z zakresu 1-128 i oznacza ilość uchwytów bloków EMB. Za każdym razem gdy wystąpi żadanie przydziału bloku pamięci rozszerzonej, przydzielany jest automatycznie uchwyt będący identyfikatorem bloku. W przypadku braku dostępnego uchwytu blok nie zostanie przydzielony. Domyślną wartością jest 32.
/INT15=n - parametr n (podawany w kilobajtach) jest liczbą z zakresu 64-65536 i oznacza ilość pamięci dostępnej dla schematu alokacji TOP-DOWN (INT 15). Domyślną wartością jest 0.
/MACHINE=n - parametr n jest kodem, który oznacza system sterowania linią A20, zależny od platformy sprzętowej. Powinien być używany w przypadku stwierdzenia problemu obsługi lini A20 przez sterownik XMM.
/A20CONTROL:ON|OFF - opcja ta determinuje w jakim przypadku kontrola nad stanem lini A20 zostanie przejęta przez sterownik XMM. Wartość ON oznacza bezwarunkowe przejęcie kontroli, natomiast wartość OFF tylko w przypadku gdy linia A20 była wcześniej zablokowana. Domyślną wartością jest ON.
/EISA - opcja ta informuje sterownik XMM, że architektura systemu jest zgodna ze standardem EISA.
/SHADOWRAM:ON|OFF - opcja ta determinuje przyłączenie obszaru shadow RAM do obsługiwanych bloków pamięci górnej. Wartość OFF oznacza dołączenie shadow RAM do UMB. Dla komputerów z pamięcią poniżej 2Mb domyślną wartością jest OFF.
/VERBOSE - opcja ta wymusza wyświetlanie wszelkich komunikatów.
EMM
Sterownik EMM znajduje się w pliku EMM386.EXE. Aby go zainstalować, należy umieścić stosowną linię w pliku CONFIG.SYS. Jej postać ukazana jest poniżej (w nawiasach kwadratowych podane są opcjonalne parametry):
DEVICE=[dysk:][ścieżka]EMM386.EXE [ON|OFF|AUTO] [memory] [MIN=n] [W=ON|OFF] [Mn|FRAME=n|/Pn] [Pn=m] [X=n-m] [I=n-m] [B=n] [L=n] [D=n] [A=n] [H=n] [RAM=n-m] [NOEMS] [NOVCPI] [HIGHSCAN] [VERBOSE] [WIN=n-m] [NOHI] [ROM=n-m] [NOMOVEXBDA] [ALTBOOT]
Poniżej znajduje się opis wszystkich parametrów sterownika EMM:
ON|OFF|AUTO - za pomocą tych parametrów można określić tryb pracy sterownika EMM. Parametry ON/OFF włączają/wyłączają mechanizmy obsługi pamięci EMS. Parametr AUTO włącza je w chwili zażądania przydziału pamięci. Domyślny jest parametr ON.
memory - parametr memory (podawany w kilobajtach) jest liczbą z zakresu 64-32768. Określa on maksymalny rozmiar pamięci XMS wykorzystywanej do implementacji pamięci EMS i VCPI. Domyślnie sterownik wykorzystuje całą dostępną pamięć.
MIN=n - parametr n (podawany w kilobajtach) jest liczbą z zakresu 64-memory. Określa on minimalny rozmiar pamięci XMS wykorzystywanej do implementacji pamięci EMS i VCPI. Obszar ten jest automatycznie alokowany co zmniejsza rozmiar pamięci XMS. Domyślną wartością jest 256.
W=ON|OFF - parametr ON uatywnia a OFF blokuje obsługę koprocesora Weitek.
Mn|FRAME=n|/Pn - parametry określają adres segmentowy ramki stron. Dla M parametr n jest kodem z zakresu 1-14 za którym kryje się zdefiniowany adres. Dla FRAME i P parametr n jest adresem segmentowym z zakresu 8000H-9000H i C000H-E000H (będącym wielokrotnością 400H). Dodatkowo dla FRAME n może mieć wartość NONE która uniemożliwia stworzenie ramki.
Pn=m - definiuje adres segmentowy strony fizycznej. Parametr n jest numerem strony z zakresu 0-255 natomiast m jest adresem segmentowym z zakresu 8000H-9C00H i C000H-EC00H (będącym wielokrotnością 400H). Strony 0-3 muszą występować po sobie kolejno i nie można ich definiować w przypadku podania adresu ramki stron (Mn|FRAME=n|/Pn).
X=n-m - blokuje tworzenie ramki stron w obszarze od adresu n do adresu m. Zakres adresów wynosi od A000H-0FFFFH. Opcja ta ma wyższy priorytet od I=n-m.
I=n-m - definiuje obszar od adresu n do adresu m w którym możliwe jest utworzenie ramki stron. Zakres adresów wynosi od A000H-0FFFFH.
B=n - parametr n określa najniższy adres segmentowy obszaru z zakresu 1000H-4000H, w którym może nastąpić wymiana stron. Domyślną wartością jest 4000H.
L=n - parametr n (podawany w kilobajtach) określa minimalny obszar pamięci XMS, która nie zostanie wykorzystana do implementacji pamięci EMS i VCPI. Domyślną wartością jest 0.
D=n - parametr n (podawany w kilobajtach) jest liczbą z zakresu 16-256 i określa rozmiar pamięci wykorzystywanej jako bufor transmisji DMA. Domyślną wartością jest 16.
A=n - parametr n jest liczbą z zakresu 0-254 i rezerwuje liczbę grup rejestrów przeznaczonych do realizacji wielozadaniowości. Domyślną wartością jest 7. Każda grupa zajmuje ok. 200 bajtów.
H=n - parametr n jest liczbą z zakresu 2-255 określającą liczbę uchwytów. Domyślną wartością jest 64.
RAM=n-m - definiuje obszar od adresu n do adresu m, który zostanie wykorzystany na bloki UMB i obsługę pamięci EMS. Domyślnie sterownik wykorzystuje cały dostępny obszar pamięci górnej.
NOEMS - wyłącza obsługę pamięci EMS.
NOVCPI - wyłącza obsługę interfejsu VCPI. Opcja musi być podana w połączeniu z opcją NOEMS. W przypadku jej podania ignorowane są parametry memory i MIN.
HIGHSCAN - opcja nakazuje przeszukanie obszarów BIOSu w celu znalezienia dodatkowych niewykorzystanych przez BIOS bloków pamięci.
VERBOSE - opcja ta wymusza wyświetlanie wszelkich komunikatów.
WIN=n-m - definiuje obszar od adresu n do adresu m do wykorzystania przez system Windows. Zakres adresów wynosi od A000H-0FFFFH. Opcja X=n-m ma wyższy priorytet, natomiast opcje RAM=n-m, ROM=n-m oraz I=n-m niższy.
NOHI - blokuje ładowanie sterownika EMM do pamięci górnej.
ROM=n-m - definiuje obszar od adresu n do adresu m który wykorzystany będzie na pamięć shadow RAM. Zakres adresów wynosi od A000H-0FFFFH.
NOMOVEXBDA - blokuje przesunięcie obszaru dodatkowych danych BIOSu z pamięci bazowej do pamięci górnej.
ALTBOOT - nakazuje przejęcie kontroli nad restartem systemu (w momencie naciśnięcia kombinacji CTRL-ALT-DEL).
2