W roku 1990 firmy: Borland, Eclipse, IGC, Intel, Lotus, Microsoft, PharLap, Phoenix, Quarterdeck i Rational opracowały specyfikację interfejsu programowego dla trybu chronionego procesora. Jest to specyfikacja DPMI (ang. DOS Protected Mode Interface). Do najpopularniejszych programów oferujących DPMI należy QEMM firmy QuarterDeck. Sama specyfikacja nie określa listy funkcji dostępnych przez przerwanie 21h, poza funkcją 4Ch. Jednak serwery DPMI poszczególnych producentów obsługują wiele z tych funkcji. QDPMI, który omówię w dalszej części artykułu, obsługuje m.in. funkcje operacji dyskowych.
Tryb chroniony pozwala nam na zaadresowanie bardzo dużej ilości pamięci, nawet gdy w systemie zainstalowano jej niewiele. Serwer DPMI udostępnia pamięć wirtualną. Jej realizacja polega na uzupełnieniu pamięci operacyjnej przez pomocniczą pamięć masową (dysk) oraz na odpowiednim zarządzaniu przesyłaniem danych przez procesor. Osobnego omówienia wymaga sprawa adresowania w trybie chronionym. Adresowanie komórki pamięci odbywa się poprzez rejestr segmentowy i rejestr indeksujący np. DS:EDX. Rejestr segmentowy DS zawiera numer selektora bloku pamięci, na którym operujemy, a rejestr EDX - 32-bitowy adres komórki w tym bloku.
Selektor zawiera numer deskryptora segmentu oraz informację dotyczące mechanizmów ochrony. Deskryptory segmentów są umieszczone w pamięci kolejno, tworząc tablice deskryptorów. W deskryptorze znajdują się informacje o segmencie. Umożliwiają one m.in. określenie segmentu jako segmentu kodu lub danych. Wykorzystywane jest to do sygnalizacji błędów, np. gdy program usiłuje zapisać coś w segmencie kodu lub wykonać segment danych. Dokładne omówienie powyższych struktur zawiera pozycja [1].
Program chcąc skorzystać z serwera DPMI powinien najpierw sprawdzić jego obecność w systemie. Serwer obsługuje przerwanie 2Fh (Standard Multiplex Interrupt) :
wywołanie : AX = 1687h
powrót : AX = 0000h - DPMI zainstalowane i wtedy :
BX - Znaczniki (Bit 0 określa czy serwer obsługuje kod 32bit)
CX - Typ procesora (2 - 286 itd.)
DX - Numer wersji DPMI
SI - Liczba paragrafów zajmowanych przez serwer
ES:DI - Adres w trybie rzeczywistym procedury przejścia
do trybu chronionego
wywołanie : AX = 1686h
powrót : AX = 0000h - Praca w trybie chronionym
AX > 0000h - Praca w trybie rzeczywistym
Aby przejść do trybu chronionego, należy wywołać procedurę, której adres otrzymaliśmy w rejestrach ES:DI. Wcześniej należy w ES umieścić segment 128 Kb bloku danych. Będzie on wykorzystywany przez serwer podczas wykonywania naszego programu. Do DS należy wpisać segment bloku danych programu. Po wywołaniu procedury w DS otrzymamy selektor do tegoż bloku danych: w ES - selektor do PSP programu, w SS - selektor do bloku stosu, w CS - selektor do bloku kodu; FS i GS będą równe zeru.
Przejście do trybu chronionego wykonuje makroinstrukcja VIRTUAL, zawarta w pliku VIRTUAL.MAC. Jest tam też zadeklarowany 128 Kb blok danych dla DPMI, struktura MEMDES oraz makroinstrukcje GETMEM i RELMEM, odpowiadające za alokację i de-alokację bloku pamięci. Wywołuje się je z parametrem będącym wskaźnikiem do struktury MEMDES. Opisuje ona blok na użytek makroinstrukcji. MEMDES ma dwa pola ważne dla programisty, są to :
- SIZE - określa wielkość bloku, jaki chcemy uzyskać; pole to inicjujemy bądź przy deklaracji struktury ( STR MEMDES <xxxx> ), bądź w trakcie wykonywania programu ( MOV [STR.SIZE].xxxx )
- SEL - po alokacji zawiera selektor bloku
Pozostałe pola zawierają uchwyt bloku i jego adres liniowy.
Przydzielenie i zawrócenie bloku o rozmiarze 1 Mb może się odbyć w ten sposób :
_blok_danych
STRUKTURA MEMDES <1024000> // definicja struktury
_blok_kodu
GETMEM STRUKTURA // przydzielenie bloku
RELMEM STRUKTURA // zwolnienie bloku
W pliku MESSAGE.MAC znajdują się pomocnicze makroinstrukcje służące do wypisywania komunikatów (mogą być one wykorzystane niezależnie) :
- MESSAGE "tekst', 1 - wypisuje string 'tekst', parametr 1 oznacza przejście do następnej linii po wypisaniu string-u, jego brak jest interpretowany jako pozostanie w bieżącej linii;
- JMESSAGE w1, w2, 'tekst1, 'tekst2' - porównuje w1 z w2; jeśli równe, wypisuje 'tekst1', w przeciwnym wypadku 'tekst'2; parametr 'tekst2' jest opcjonalny;
- ERRORMESSAGE 'tekst1, 'tekst2' - jeśli ustawiony CF, to wypisuje 'tekst1' i wywołuje funkcję 4Ch przerwania 21h, w przeciwnym wypadku wypisuje 'tekst2';
- COUTW x, 1 - wypisuje 16-bitowe słowo x, 1 - opis jw.;
- COUTD x, 1 - wypisuje 32-bitowe słowo z, 1 - jw;
- REGMESSAGE - wypisuje stan rejestrów segmentowych.
Plik EXAMPLE.ASM zawiera program przykładowy, który wczytuje w całości do pamięci plik TEST.BIN, a następnie wypisuje jego pierwsze i ostatnie słowo (word). Program testowany był na komputerze z 8 MB RAM, a plik TEST.BIN miał 10 Mb.
Zainteresowanym proponuję napisanie programu wykonującego dokładnie te same operacje wraz ze wczytaniem pliku w całości do pamięci w rzeczywistym trybie procesora.
Opis ważniejszych funkcji DPMI (numer funkcji umieszcza się w AX; każda z funkcji w przypadku niepowodzenia ustawia CF) :
Funkcja 0000h ; przydzielenie deskryptorów w tablicy LDT
; (lokalna tablica deskryptorów)
Wywołanie : CX - liczba przydzielanych deskryptorów
Powrót : AX - selektor pierwszego deskryptora
----------------------------------------------------------
Funkcja 0001h ; zwolnienie deskryptora w tablicy LD
Wywołanie : BX - selektor zwalnianego deskryptora
----------------------------------------------------------
Funkcja 0006h ; uzyskanie adresu liniowego bloku pamięci
; z deskryptora
Wywołanie : BX - selektor
Powrót : CX:DX - adres liniowy bloku
----------------------------------------------------------
Funkcja 0007h ; ustawienie adresu liniowego bloku pamięci
; w deskryptorze
Wywołanie : BX - selektor
CX:DX - adres liniowy bloku
----------------------------------------------------------
Funkcja 0008h ; ustawienie wielkości pamięci
; w deskryptorze
Wywołanie : BX - selektor
CX:DX - wielkość bloku w bajtach
----------------------------------------------------------
Funkcja 0501h ; przydzielenie bloku pamięci
Wywołanie : BX:CX - wielkość bloku w bajtach
Powrót : BX:CX - adres liniowy bloku
: Si:DI - uchwyt bloku
----------------------------------------------------------
Funkcja 0502h ; zwolnienie bloku pamięci
Wywołanie : SI:DI - uchwyt bloku
----------------------------------------------------------
Funkcja 0503h ; zmiana długości bloku pamięci
Wywołanie : BX:DX - nowa długość
SI:DI - uchwyt bloku
Powrót : BX:CX - nowy adres liniowy bloku
SI:DI - nowy uchwyt bloku
----------------------------------------------------------
Funkcja 0800h ; odwzorowanie adresu fizycznego
Wywołanie : BX:CX - adres fizyczny bloku
SI:DI - rozmiar obszaru danych
Powrót : BX:CX - adres liniowy odwzorowany na
wskazany adres fizyczny
----------------------------------------------------------
Dokładniejszy opis funkcji DPMI zawiera pozycja [2].
Literatura :
[1] - R. Goczyński, M. Tuszyński, "Mikrokomputery 80286, 80386 i i486", Help
[2] - L. Bułhak, R. Goczyński, M. Tuszyński, DOS 5.00 od środka", Help
[3] - Gary Syck, "Turbo Assembler Biblia Użytkownika", LT&P.
Wydruk 1.
STRUC MEMDES
SIZE DD 0
SEL DW 0
DW 0
DW 0
DW 0
DW 0
ENDS
SEGMENT DPMI DWORD USE32
VBUFFER DB 128000 DUP(0)
ENDS
MACRO GETMEM W
PUSH EBX
PUSH ECX
PUSH EDX
MOV AX, 0000h
MOV CX, 1
INT 31h
MOV [WORD PTR W+4], AX
ERRORMESSAGE 'GETMEM: ERROR IN FUNCTION 0x0000'
MOV AX, 0501h
MOV EBX, [DWORD PTR W+0]
MOV CX, BX
SHR EBX, 16
INT 31h
MOV [WORD PTR W+6], BX
MOV [WORD PTR W+8], CX
MOV [WORD PTR W+10], SI
MOV [WORD PTR W+12], DI
ERRORMESSAGE 'GETMEM: ERROR IN FUNCTION 0x0501'
MESSAGE 'MEMEORY GET: '
MOV EAX, [DWORD PTR W+0]
COUTD EAX, 1
MOV AX, 0007h
MOV DX, [WORD PTR W+8]
MOV CX, [WORD PTR W+6]
MOV BX, [WORD PTR W+4]
INT 31h
ERRORMESSAGE 'GETMEM: ERROR IN FUNCTION 0x0007'
MOV AX, 0008h
MOV ECX, [DWORD PTR W+0]
MOV DX, CX
SHR ECX, 16
INT 31h
ERRORMESSAGE 'GETMEM: ERROR IN FUNCTION 0x0008'
POP EDX
POP ECX
POP EBX
ENDM
MACRO RELMEM W
MOV AX, 0502h
MOV SI, [WORD PTR W+10]
MOV DI, [WORD PTR W+12]
INT 31h
ERRORMESSAGE 'RELMEM: ERROR IN FUNCTION 0x0502'
MESSAGE 'MEMORY RELASED: '
MOV EAX, [DWORD PTR W+0]
COUTD EAX, 1
MOV AX, 0001h
MOV BX, [WORD PTR W+4]
INT 31h
ERRORMESSAGE 'RELMEM: ERROR IN FUNCTION 0x0001'
ENDM
MACRO VIRTUAL
LOCAL START, DPMI_OK
JMP START
GO32 DD 0
START:
MOV AX, 1687h
INT 2Fh
JMESSAGE AX, 0, 'DPMI Detetcet', DPMI Not Detected ...'
CMP AX, 0
JE DPMI_OK
MOV AH, 4Ch
INT 21h
DPMI_OK:
JMESSAGE CL, 2, 'CPU 286'
JMESSAGE CL, 3, 'CPU 386'
JMESSAGE CL, 4, 'CPU 486'
MOV [WORD PTR GO32+2], ES
MOV [WORD PTR GO32], DI
MOV AX, 1686h
INT 2Fh
JMESSAGE AX, 0, 'VIRTUAL', 'REAL'
CMP AX, 0
JE INVIRTUAL
MOV AX, SEG VBUFFER
MOV ES, AX
MOV AX, 1
REGMESSAGE
CALL [GO32]
INVIRTUAL:
MOV AX, 1686h
INT 2Fh
JMESSAGE AX, 0, 'VIRTUAL', 'REAL'
REGMESSAGE
ENDM
Wydruk 2.
MACRO MESSAGE STRING, ENDL
LOCAL MEND, MESS
PUSH AX
PUSH DX
PUSH DS
MOV AH, 09h
MOV DX, CS
MOV DS, DX
MOV DX, OFFSET MESS
INT 21h
JMP MEND
MESS DB STRING, '$'
MEND:
IFNB <ENDL>
MOV AH, 02h
MOV DL, 10
INT 21h
MOV DL, 13
INT 21h
ENDIF
POP DS
POP DX
POP AX
ENDM
MACRO JMESSAGE W1, W2, STRING1, STRING2
LOCAL MEND, ME2
PUSHF
CMP W1,W2
JNE ME2
MESSAGE STRING1, 1
JMP MEND
ME2:
IFNB <STRING2%gt;
MESSAGE STRING2, 1
ENDIF
MEND:
POPF
ENDM
MACRO ERRORMESSAGE STRING1, STRING2
LOCAL ME2
JNC ME2
MESSAGE STRING1, 1
MOV AH,4Ch
INT 21h
ME2:
IFNB <STRING2>
MESSAGE STRING2, 1
ENDIF
ENDM
MACRO COUTH W
LOCAL LETTER, COMMON
MOV DL,W
AND DL,00001111b
CMP DL,9
JA LETTER
AND DL,48
JMP COMMON
LETTER:
ADD DL,55
COMMON:
MOV AH,2
INT 21h
ENDM
MACRO COUTW W, ENDL
PUSHF
PUSH AX
PUSH BX
PUSH DX
MOV BX,W
MOV AH,02h
MOV DL,'0'
INT 21h
MOV DL,'x'
INT 21h
MOV AX,BX
SHR AX,12
COUTH AL
MOV AX,BX
SHR AX,8
COUTH AL
MOV AX,BX
SHR AX,4
COUTH AL
MOV AX,BX
COUTH AL
IFNB <ENDL>
MOV AH,02h
MOV DL,10
INT 21h
MOV DL,13
INT 21h
ENDIF
POP DX
POP BX
POP AX
POPF
ENDM
MACRO COUTD W, ENDL
PUSHF
PUSH AX
PUSH BX
PUSH DX
MOV EBX,W
MOV AH,02h
MOV DL,'0'
INT 21h
MOV DL,'x'
INT 21h
MOV EAX,EBX
SHR EAX,28
COUTH AL
MOV EAX,EBX
SHR EAX,24
COUTH AL
MOV EAX,EBX
SHR EAX,20
COUTH AL
MOV EAX,EBX
SHR EAX,16
COUTH AL
MOV EAX,EBX
SHR EAX,12
COUTH AL
MOV EAX,EBX
SHR EAX,8
COUTH AL
MOV EAX,EBX
SHR EAX,4
COUTH AL
MOV EAX,EBX
COUTH AL
IFNB <ENDL>
MOV AH,02h
MOV DL,10
INT 21h
MOV DL,13
INT 21h
ENDIF
POP DX
POP BX
POP AX
POPF
ENDM
MACRO REGMESSAGE
MESSAGE 'CS = '
COUTW CS
MESSAGE 'SS = '
COUTW SS
MESSAGE 'DS = '
COUTW DS
MESSAGE 'ES = '
COUTW ES
MESSAGE 'FS = '
COUTW FS
MESSAGE 'GS = '
COUTW GS,1
ENDM
Wydruk 3.
IDEAL P386 MODEL LARGE STACK 1024 ASSUME CS:KOD, GS:DANE INCLUDE "VIRTUAL.MAC" INCLUDE "MESSAGE.MC" SEGMENT DANE DWORD USE32 _DANE: MEMDES1 MEMDES<> FILE DB 'TEST.BIN',0 HANDLE DW 0 ENDS SEGMENT KOD DWORD START: MOV DX,SEG _DANE MOV DS,DX VIRTUAL MOV DX,DS MOV GS,DX MOV AX,3D02h MOV EDX,OFFSET FILE INT 21h MOV [HANDLE],AX ERRORMESSAGE 'OPEN FILE: FILE NOT FOUND ' MOV AX,4202h MOV BX,[HANDLE] XOR DX,DX XOR CX,CX INT 21h MOV CX,CX SHL ECX,16 MOV CX,AX PUSH ECX PUSH ECX MOV [MEMDES1.SIZE],ECX GETMEM MEMDES1 MOV DX,[MEMDES1.SEL] MOV DS,DX MOV AX,4200h MOV BX,[HANDLE] XOR DX,DX XOR CX,CX INT 21h MOV AX,3F00h MOV BX,[HANDLE] POP ECX MOV EDX,0 INT 21h MOV AX,[WORD PTR DS:0] COUTW AX,1 POP ECX SUB ECX,2 MOV AX,[WORD PTR DS:ECX] COUTW AX,1 MOV AH,4Ch INT 21h ENDS END START
Sebastian Majewski, "PC Kurier", 29 Sierpnia 1996r.