1896 - DPMI - Własny DOS Extender

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.