Программирование Базы данных
| |
Приведённая ниже
программа демонстрирует использование
функций интерфейса 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);
}
[ << Назад ] [ Содержание ] [ Далее >> ]
|