Защищенный режим процессоров 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 = ®s;
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);
}
|

