Главная ]
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);

}

 

<< Назад ] Содержание ] Далее >> ]

Дизайн: Piton Alien
Rambler's Top100 Рейтинг@Mail.ru
Сайт создан в системе uCoz