Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Защищенный режим процессоров Intel 80286/80386/80486

© Александр Фролов, Григорий Фролов
Том 4, М.: Диалог-МИФИ, 1993, 234 стр.

[Назад] [Содеожание] [Дальше]

7.1. Использование функций DPMI

Приведённая ниже программа демонстрирует использование функций интерфейса DPMI, описанного в предыдущей главе. Эта программа может работать только под управлением WINDOWS версий 3.0, 3.1 и только в расширенном режиме на процессорах i80386 или i80486. Такое ограничение связано с тем, что только в расширенном режиме существует понятие виртуальной DOS-машины, и только в этом режиме DOS-программа может воспользоваться сервисом DPMI.

Вы можете также попробовать запустить эту программу под управлением DOS-экстендера, входящего в состав интегрированной системы разработки программ Borland C++ 3.1. Запустите программу DPMIRES.EXE, входящую в состав Borland C++ 3.1, и затем - программу, приведённую ниже. (DOS-экстендеры, входящие в состав Borland C++ 2.0 или 3.0, не вполне совместимы с DPMI, поэтому наш пример с этими системами работать не будет).

Программа начинает свою работу с проверки доступности сервера DPMI, при этом не делается никаких предположений относительно средств, обеспечивающих присутствие DPMI. Это означает, что вы можете проверить работоспособность этой программы в различных средах, предоставляющих интерфейс DPMI, например на виртуальной DOS-машине операционной системы OS/2 версии 2.0.

Программа демонстрирует возможность вызова в защищённом режиме прерываний реального режима. В первой части программы вывод на экран и ввод с клавиатуры выполняется в защищённом режиме, но с использованием привычных вам прерываний реального режима.

Во второй части программы демонстрируется непосредственная запись в видеопамять. При этом для адресации видеопамяти программа заказывает селектор в таблице LDT с помощью специально предназначенных для этого функций DPMI.

Листинг 21. Использование интерфейса DPMI
Файл dpmi.c
-----------------------------------------------------------


#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include <stdarg.h>

typedef struct {
         unsigned long edi, esi, ebp, reserved, ebx, edx, ecx, eax;
         unsigned flags, es, ds, fs, gs, ip, cs, sp, ss;
} RM_INT_CALL;

#define MONO_MODE           0x07
#define BW_80_MODE          0x02
#define COLOR_80_MODE       0x03

// Макро для вычисления линейного адреса исходя из
// логического адреса реального режима

#define ABSADDR(seg, ofs) \
         ((((unsigned long) seg) << 4) + ((ofs) & 0xFFFF))

typedef struct {                                  // байт доступа
         unsigned accessed   : 1;
         unsigned read_write : 1;
         unsigned conf_exp   : 1;
         unsigned code       : 1;
         unsigned xsystem    : 1;
         unsigned dpl        : 2;
         unsigned present    : 1;
} ACCESS;

typedef struct {                 // дескриптор
         unsigned        limit;
         unsigned        addr_lo;
         unsigned char   addr_hi;
         ACCESS          access;
         unsigned        reserved;
} DESCRIPTOR;

// Структура для записи информации о памяти

typedef struct {
        unsigned long avail_block;
        unsigned long max_page;
        unsigned long max_locked;
        unsigned long linadr_space;
        unsigned long total_unlocked;
        unsigned long free_pages;
        unsigned long tot_phys_pages;
        unsigned long free_linspace;
        unsigned long size_fp;
        char reserved[12];
} PMI;


void dos_exit(unsigned);
void dpmi_init(void);
void set_pmode(void);
void cdecl pm_printf(const char *, ...);
void pm_puts(char *);
void pm_putch(int);
int  rm_int(unsigned, unsigned , RM_INT_CALL far *);
int  mi_show(void);
unsigned get_sel(void);
int set_descriptor(unsigned pm_sel, DESCRIPTOR far *desc);
void vi_print(unsigned int x, unsigned int y, char *s, char attr);
void vi_hello_msg(void);



void main() {

        clrscr();
        printf("DOS Protected Mode Interface Demo, © Frolov A.V., 1992\n\r"
"--------------------------------------------------------\n\r\n\r");

// Определяем текущий видеорежим и
// сегментный адрес видеобуфера

        video_init();

// Инициализируем защищённый режим

        dpmi_init();

 printf("\n\r\n\r\n\rДля входа в защищённый режим нажмите любую клавишу...");
        getch();

// Входим в защищённый режим

        set_pmode();

// Стираем экран и выводим сообщение, находясь в
// защищённом режиме. Пользуемся выводом через
// эмулируемое прерывание реального режима DOS

        textcolor(BLACK);       textbackground(LIGHTGRAY);      clrscr();
        pm_printf(" Установлен защищённый режим работы процессора!\n\r"
         " ----------------------------------------------\n\r\n\r");

// Выводим текущую информацию о распределении памяти

        mi_show();

        pm_printf("\n\r\n\r\n\r Для продолжения нажмите любую клавишу...");
        getch();

        clrscr();

// Получаем селектор для непосредственного доступа к видеопамяти

        alloc_videosel();

        pm_printf("\n\r\n\r\n\r Для продолжения нажмите любую клавишу...");
        getch();
        clrscr();

// Выводим сообщения, пользуясь непосредственным доступом
// к видеопамяти

        vi_hello_msg();
        vi_print(0, 3,
                " Для возврата в реальный режим нажмите любую клавишу", 0x7f);
        getch();

// Освобождаем полученный селектор

        free_videosel();

        textcolor(LIGHTGRAY);   textbackground(BLACK);  clrscr();

// Завершаем работу программы выходом в DOS

        dos_exit(0);
}

// -------------------------------------------------
// Процедура для завершения работы программы
// -------------------------------------------------

void dos_exit(unsigned err) {
         asm mov ax, err
         asm mov ah, 04ch
         asm int 21h
}

// -------------------------------------------------
// Инициализация для работы с DPMI
// -------------------------------------------------

        union REGS inregs, outregs;
        struct SREGS segregs;
        void (far *pm_entry)();
        unsigned hostdata_seg, hostdata_size, dpmi_flags;

void dpmi_init(void) {

// Проверяем доступность и параметры сервера DPMI

        inregs.x.ax = 0x1687;
        int86x(0x2F, &inregs, &outregs, &segregs);
        if(outregs.x.ax != 0) {
                printf("Сервер DPMI не активен."); exit(-1);
        }

// Определяем версию сервера DPMI

        printf("Версия сервера DPMI: \t\t\t%d.%d\n",
                outregs.h.dh, outregs.h.dl);

// Определяем тип процессора

        printf("Тип процессора:\t\t\t\t");
        if(outregs.h.cl == 2) printf("80286");
        else if(outregs.h.cl == 3) printf("80386");
        else if(outregs.h.cl == 4) printf("80486");

// Определяем возможность работы с 32-разрядными
// программами

        dpmi_flags = outregs.x.bx;
        printf("\nПоддержка 32-разрядных программ:\t");
        if(dpmi_flags && 1) printf("ПРИСУТСТВУЕТ");
        else printf("ОТСУТСТВУЕТ");

// Определяем размер области памяти для сервера DPMI

        hostdata_size = outregs.x.si;
        printf("\nРазмер памяти для сервера DPMI:\t\t%d байт",
                hostdata_size * 16);

// Определяем адрес точки входа в защищённый режим

        FP_SEG(pm_entry) = segregs.es;
        FP_OFF(pm_entry) = outregs.x.di;
        printf("\nАдрес точки входа в защищённый режим: \t%Fp\n",
                pm_entry);

// Заказываем память для сервера DPMI

        if(hostdata_size) {
                if(_dos_allocmem(hostdata_size, &hostdata_seg) != 0) {
                        printf("Мало стандартной памяти"); exit(-1);
                }
        }

}

// ------------------------------------------------
// Процедура для установки защищённого режима
// ------------------------------------------------

void set_pmode() {

// Входим в защищённый режим

         asm {
                  mov ax, hostdata_seg
                  mov es, ax
                  mov ax, dpmi_flags
         }
         (*pm_entry)();

}

// -------------------------------------------
// Процедура вывода символа на экран в
// защищённом режиме
// -------------------------------------------

void pm_putch(int chr) {

// Структура для вызова прерывания должна
// быть определена как статическая

         static RM_INT_CALL regs;
         static RM_INT_CALL far *pregs = (void far *) 0;

// В первый раз инициализируем структуру
// и указатель на неё

         if (!pregs) {
                  pregs = &regs;
                  memset(pregs, 0, sizeof(RM_INT_CALL));
                  regs.eax = 0x0200;
         }
         regs.edx = chr;

// Вызываем прерывание DOS для вывода символа

         rm_int(0x21, 0, pregs);
}

// -------------------------------------------
// Процедура вывода строки на экран в
// защищённом режиме
// -------------------------------------------

void pm_puts(char *str_ptr) {
         while (*str_ptr) { pm_putch(*str_ptr); str_ptr++; }
}

// -------------------------------------------
// Процедура вывода строки на экран в
// защищённом режиме, аналог функции printf()
// -------------------------------------------

void cdecl pm_printf(const char *fmt, ...)
{
         char buffer[512], *sptr=buffer;
         va_list marker;
         va_start(marker, fmt);
         vsprintf(buffer, fmt, marker);
         va_end(marker);
         while (*sptr) pm_putch(*sptr++);
}

// --------------------------------------------
// Процедура вызова прерывания реального режима
// --------------------------------------------

int rm_int(unsigned int_number, // номер прерывания
                          unsigned params,    // количество слов параметров,
                                               // передаваемых через стек
                          RM_INT_CALL far *rm_call) // адрес структуры
                                              // для вызова прерывания
{
         asm {
                  push di
                  push bx
                  push cx
                  mov ax, 0300h      // функция вызова прерывания
                  mov bx, int_number
                  mov cx, params;
                  les di, rm_call          // запись в ES:DI адреса структуры
                  int 31h            // вызов сервера DPMI
                  jc error
                  mov ax, 0               // нормальное завершение
                  jmp short rm_int_end
         }
error:  asm mov ax, 0          // завершение с ошибкой
rm_int_end:   asm pop cx
                  asm pop bx
                  asm pop di
}

// -----------------------------------------------------
// Процедура отображает текущее состояние памяти
// -----------------------------------------------------

int mi_show(void) {

         PMI minfo, far *minfoptr = &minfo;
         unsigned long psize, far *psizeptr=&psize;
         unsigned sel;
         void far *fp;


         get_mi(minfoptr);

         pm_printf(" Информация об использовании памяти\n\r"
            " ----------------------------------\n\r"
         "\r\n Размер максимального доступного блока:\t\t%ld байт"
         "\r\n Доступно незафиксированных страниц:\t\t%ld",
         minfo.avail_block,
         minfo.max_page);

         pm_printf("\r\n Доступно зафиксированных страниц:\t\t%ld"
         "\r\n Размер линейного адресного пространства:\t%ld страниц"
         "\r\n Всего имеется незафиксированных страниц:\t%ld",
         minfo.max_locked,
         minfo.linadr_space,
         minfo.total_unlocked);

         pm_printf("\r\n Количество свободных страниц:\t\t\t%ld"
         "\r\n Общее количество физических страниц:\t\t%ld",
         minfo.free_pages,
         minfo.tot_phys_pages);

     pm_printf("\r\n Свободное линейное адресное пространство:\t%ld страниц"
         "\r\n Размер файла/раздела для страничного обмена:\t%ld страниц",
         minfo.free_linspace,
         minfo.size_fp);

         get_page_size(psizeptr);
         pm_printf("\r\n Размер страницы:\t\t\t\t%ld байт\r\n", psize);

// Выводим текущие значения регистров CS и DS

         asm mov sel,cs
         pm_printf("\n\r CS = %04.4X,   ",sel);
         asm mov sel,ds
         pm_printf("DS = %04.4X",sel);

// Выводим значение текущего приоритетного кольца

         fp = (void far *) main;
         sel = FP_SEG(fp) & 3;
         pm_printf("\n\r Номер приоритетного кольца = %d\n\r",sel);

}

// -----------------------------------------------
// Процедура для получения информации об
// использовании памяти
// -----------------------------------------------

int get_mi(PMI far *minfo) {
        asm {
                  mov ax, 0500h
                  les di, minfo       // ES:DI = адрес структуры DMI
                  int 31h
                  jc error
                  mov ax, 0
                  jmp short get_mi_end
        }
        error: asm mov ax, 1
        get_mi_end:
}

// ------------------------------------------------
// Процедура для получения размера страницы памяти
// ------------------------------------------------

int get_page_size(long far *page_size) {
        asm {
                  mov ax, 0604h
                  int 31h
                  jc error

                  les di, page_size // ES:DI = адрес page_size
                  mov es:[di], cx
                  mov es:[di+2], bx

                  mov ax, 0
                  jmp short gps_end
        }
        error: asm mov ax, 1
        gps_end:
}

// --------------------------------------------------
// Определение сегментного адреса видеопамяти
// --------------------------------------------------

unsigned crt_mode, crt_seg;

int video_init(void) {

        union REGS r;

// Определяем текущий видеорежим

        r.h.ah=15;
        int86(0x10,&r,&r);
        crt_mode = r.h.al;

        if(crt_mode == MONO_MODE) crt_seg = 0xb000;
        else if(crt_mode == BW_80_MODE || crt_mode == COLOR_80_MODE)
                crt_seg = 0xb800;
        else {
                printf("\nИзвините, этот видеорежим недопустим.");
                exit(-1);
        }
}

// ---------------------------------------------------
// Получение селектора для адресации видеопамяти
// ---------------------------------------------------

char far *vid_ptr;
DESCRIPTOR d;
unsigned ldtsel;

int alloc_videosel(void) {

        void far *fp;
        unsigned long addr;

        FP_SEG(vid_ptr) = crt_seg;
        FP_OFF(vid_ptr) = 0;
     pm_printf(" Адрес видеопамяти реального режима:\t %Fp\r\n", vid_ptr);

// Получаем свободный LDT-селектор

        if (! (ldtsel = get_sel())) {
          pm_printf(" Ошибка при получении селектора");
          dos_exit(-1);
        }
        pm_printf(" Получен селектор:\t\t\t%04.4X\n\r", ldtsel);

// Подготавливаем дескриптор для полученного селектора

        d.limit = 0x2000;
        addr = ABSADDR(crt_seg, 0);
        d.addr_lo = addr & 0xFFFF;
        d.addr_hi = addr >> 16;
        d.access.accessed = 0;      // не использовался
        d.access.read_write = 1;    // разрешены чтение/запись
        d.access.conf_exp = 0;      // не стек
        d.access.code = 0;          // это сегмент данных
        d.access.xsystem = 1;       // не системный дескриптор
        d.access.dpl = 3;                         // приоритетное кольцо 3
        d.access.present = 1;       // сегмент присутствует в памяти
        d.reserved = 0;

// Устанавливаем дескриптор

        if (!set_descriptor(ldtsel, &d)) {
          pm_printf(" Ошибка при установке дескриптора"); getch();
          dos_exit(-1);
         }

// Выводим на экран адрес видеопамяти

         FP_SEG(vid_ptr) = ldtsel;
         FP_OFF(vid_ptr) = 0;
         pm_printf(" Адрес видеопамяти защищённого режима:\t%Fp\r\n", 
		 vid_ptr);
}

// --------------------------------------------------
// Освобождение селектора видеопамяти
// --------------------------------------------------

int free_videosel(void) {
         if (!sel_free(ldtsel)) {
                        pm_printf(" Ошибка при освобождении селектора");
                        dos_exit(-1);
         }
}

// ----------------------------------------------
// Получить один селектор в LDT
// ----------------------------------------------

unsigned get_sel(void) {
         asm {
                  mov ax, 0        // получить селектор
                  mov cx, 1        // нужен один селектор
                  int 31h
                  jc error
                  jmp short gs_end // AX содержит новый LDT-селектор
         }
error: asm mov ax, 0     // произошла ошибка
gs_end:
}

// --------------------------------------------------
// Установить дескриптор для LDT-селектора
// --------------------------------------------------

int set_descriptor(unsigned pm_sel, DESCRIPTOR far *desc) {

         asm {
                  push di
                  push bx
                  mov ax, 000Ch 
                  mov bx, pm_sel
                  les di, desc  
                  int 31h      
                  jc error
                  mov ax, 1    
                  jmp short sd_end
          }
error:  asm mov ax, 0    
sd_end: asm pop bx
                  asm pop di
}

// --------------------------------------------------
// Освободить LDT-селектор
// --------------------------------------------------

int sel_free(unsigned pmodesel) {
         asm {
                  mov ax, 0001h
                  mov bx, pmodesel
                  int 31h         
                  jc error
                  mov ax, 1       
                  jmp short done
          }
error: asm mov ax, 0        
done:
}

// -------------------------------------------------------
// Вывод символа непосредственной записью в видеопамять
// -------------------------------------------------------

void vi_putch(unsigned int x, unsigned int y ,char c, char attr) {

        register unsigned int offset;
        char far *vid_ptr;

        offset=(y*160) + (x*2);
        vid_ptr=MK_FP(ldtsel, offset);
        *vid_ptr++=c; *vid_ptr=attr;
}
// -------------------------------------------------------
// Вывод строки непосредственной записью в видеопамять
// -------------------------------------------------------

void vi_print(unsigned int x, unsigned int y, char *s, char attr) {
        while(*s) vi_putch(x++, y, *s++, attr);
}
// -------------------------------------------------------
// Вывод сообщения непосредственной записью в видеопамять
// -------------------------------------------------------

void vi_hello_msg(void) {

        vi_print(0, 0,
                " Демонстрация работы с интерфейсом "
                "DPMI   ¦ © Frolov A.V., 1992 ", 0x30);

}




[Назад] [Содеожание] [Дальше]