В этой таблице описан формат регистра флагов
для процессоров i80386 и i80486 (регистр флагов
процессора i80286 называется FLAGS и представляет
собой младшее 16-разрядное слово регистра EFLAGS):
Номер бита |
Назначение |
0 - CF |
Флаг переноса |
1 - 1 |
Зарезервировано и равно 1 |
2 - PF |
Флаг чётности |
3 - 0 |
Зарезервировано и равно 0 |
4 - AF |
Флаг вспомогательного переноса |
5 - 0 |
Зарезервировано и равно 0 |
6 - ZF |
Флаг нуля |
7 - SF |
Флаг знака |
8 - TF |
Флаг ловушки |
9 - IF |
Флаг разрешения прерываний |
10 - DF |
Флаг направления |
11 - OF |
Флаг переполнения |
12-13 - IOPL |
Уровень привилегий ввода/вывода |
14 - NT |
Флаг вложенной задачи |
15 - 0 |
Зарезервировано и равно 0 |
16 - RF |
Флаг возобновления (только i80386 и i80486) |
17 - VM |
Флаг режима виртуального процессора 8086
(только i80386 и i80486) |
18 - AC |
Флаг проверки выравнивания (только i80486) |
19-31 - 0 |
Зарезервировано и равно 0 |
Регистр |
Назначение |
CR0 |
Регистр состояния процессора |
CR1 |
Зарезервирован |
CR2 |
Линейный адрес отказа страницы |
CR3 |
Базовый адрес каталога страницы |
Номер бита |
Назначение |
0 - PE |
Включение защищённого режима работы
процессора. |
1 - MP |
Присутствие сопроцессора. |
2 - EM |
Эмуляция сопроцессора. |
3 - TS |
Переключение задачи. |
4 - ET |
Тип сопроцессора - i80287 или i80387. |
5-14 |
Зарезервировано |
15 - PG |
Включение механизма трансляции страниц |
Номер бита |
Назначение |
0 - PE |
Включение защищённого режима работы
процессора. |
1 - MP |
Присутствие сопроцессора. |
2 - EM |
Эмуляция сопроцессора. |
3 - TS |
Переключение задачи. |
4 - ET |
Тип сопроцессора - i80287 или i80387. |
5 - NE |
Числовая ошибка. Разрешает обработку
ошибок при операциях с плавающей точкой. |
6-15 |
Зарезервировано |
16 - WP |
Защита записи. При установке этого бита
страницы пользователя защищены от записи в
режиме супервизора. |
17 |
Зарезервировано |
18 - AM |
Бит маски выравнивания. Этот бит
разрешает или запрещает контроль выравнивания
операндов команд в памяти. Контроль выравнивания
разрешён только для программ, работающих в
третьем кольце, при условии что установлен бит AM. |
19-28 |
Зарезервировано |
29 - NW |
Разрешение сквозной записи.
Используется в механизме управления
кэшированием. |
30 - CD |
Запрещение кэширования. Если этот бит
установлен, внутреннее кэширование запрещено. |
31 - PG |
Включение механизма трансляции страниц |
Номер бита |
Назначение |
0-2 |
Зарезервировано |
3 - PWT |
Прозрачность записи на уровне страниц.
Используется для управления записью во внешний
кэш. |
4 - PCD |
Запрещение кэширования на уровне
страниц. Используется для управления работой
внешнего кэша. |
5-31 - PBDR |
Базовый регистр каталога страниц |
Системные команды предназначены для использования, главным образом, в модулях операционных систем (в модулях ядра операционной системы, в драйверах и т.д.). Некоторые из перечисленных ниже команд полезны и при разработке прикладных программ, работающих в защищённом режиме. Мы приведём только краткий перечень основных системных команд, подробности вы можете узнать из справочных руководств по процессорам (см. список литературы).
Как правило, системные команды могут использовать только те программы, которые выполняются в нулевом привилегированном кольце.
Эта команда используется системными модулями для проверки уровня запрашиваемых привилегий в передаваемых им в качестве параметров селекторов. Прикладная программа не должна запрашивать привилегии, превышающие её собственные.
Первый операнд команды - 16-разрядный регистр или слово памяти, содержащие значение проверяемого селектора. Второй операнд - регистр, в который записано содержимое CS прикладной программы.
Если команда не изменяла уровень привилегий, в регистре FLAGS (EFLAGS для процессоров i80386 и i80486) устанавливается флаг нуля. В противном случае этот флаг сбрасывается.
Пример использования команды:
mov dx, cs mov ax, TESTED_SELECTOR arpl dx, ax
Каждый раз при переключении задачи флаг TS устанавливается в 1. Команда CLTS позволяет сбросить этот флаг.
Для процессора i80286 команда LAR загружает в первый операнд (регистр) байт доступа дескриптора, выбираемого вторым операндом. Второй операнд является селектором, указывающим на используемый дескриптор.
В процессорах i80386 и i80486 команда LAR использует в качестве первого операнда 32-разрядный регистр. Кроме байта прав доступа в этот регистр заносятся биты типа сегмента (9-11), DPL (14), бит присутствия (15), бит дробности (23).
Команда выполняет инициализацию регистра GDTR, указывающего расположение в памяти и размер глобальной таблицы дескрипторов.
Команда выполняет инициализацию регистра IDTR, указывающего расположение в памяти и размер дескрипторной таблицы прерываний.
Команда выполняет инициализацию регистра LDTR, указывающего расположение в памяти и размер локальной таблицы дескрипторов.
С помощью этой команды можно выполнить загрузку младшего слова регистра CR0 из регистра - операнда команды.
Эта команда может использоваться для переключения процессора в защищённый режим. Обратного переключения эта команда не обеспечивает (даже для процессоров i80386 и i80486).
Команда имеет два операнда. Граница сегмента, селектор которого используется в качестве второго операнда (задаётся в регистре), загружается в регистр, указанный в качестве первого операнда.
Команда предназначена для загрузки регистра TR - регистра задачи. Загрузка этого регистра не приводит к переключению задачи.
Для процессоров i80386 и i80486 в качестве операндов обычной команды MOV допустимо (на нулевом уровне привилегий) указывать системные регистры - CR0, CR2, CR3, DR0, DR1, DR2, DR3, DR6, DR7, TR6, TR7. Команда MOV может быть использована процессорами i80386 и i80486 для возврата процессора из защищённого режима в реальный.
Команда позволяет узнать текущее содержимое регистра глобальной дескрипторной таблицы GDTR, обычное её используют в системных отладчиках.
Команда позволяет узнать текущее содержимое регистра глобальной дескрипторной таблицы прерываний IDTR, используется в системных отладчиках.
Команда позволяет узнать текущее содержимое регистра локальной дескрипторной таблицы LDTR, используется в системных отладчиках.
Команда записывает в память или 16-битовый регистр младшее слово регистра CR0 и может быть использована в системных отладчиках.
Команда записывает текущее содержимое регистра задачи TR в 16-разрядную ячейку памяти или 16-разрядный регистр. Может использоваться в системных отладчиках.
С помощью этих двух команд можно проверить доступность выбранного селектором сегмента на чтение и запись, соответственно. Если операция чтения или записи доступна, флаг нуля ZF устанавливается в единицу, в противном случае он сбрасывается в ноль.
Основное назначение этой команды - предотвратить возникновение исключения по защите памяти при попытке обращения к сегменту. Прежде чем выполнять обращение, программа может проверить доступность сегмента и сделать соответствующие выводы.
Оказывается, для процессора i80286 существует способ получения доступа к расширенной памяти, не переключаясь в защищённый режим. Для этого может быть использована недокументированная команда LOADALL, имеющая код 0F05h (команда не имеет операндов). Эта команда не описана в справочниках по процессору i80286, информация о ней поставляется фирмой Intel по запросу. Те сведения о команде LOADALL, которые приведены в нашей книге, получены по электронной почте из BBS и могут быть использованы только для расширения вашего кругозора и для оценки полезности этой команды в ваших разработках.
Команда LOADALL первоначально была задумана фирмой Intel как тестовая. Однако оказалось, что она пригодна и для обращения к расширенной памяти в реальном режиме. Широко известный драйвер расширенной памяти HIMEM.SYS обращается в область адресов выше первого мегабайта именно с помощью команды LOADALL (а не переключаясь в защищённый режим и возвращаясь обратно, как это можно было бы предположить).
Команда LOADALL сокращает время, требуемое драйверу HIMEM.SYS на доступ к расширенной памяти, так как время на переключение в защищённый режим и обратное переключение достаточно велико по сравнению с временем, необходимым на копирование данных из основной памяти в расширенную или обратно.
Другое применение команды - драйвер электронного диска Microsoft RAMDRIVE.SYS и блок совместимости операционной системы Microsoft OS/2 версии 1.x.
Секрет команды LOADALL заключается в том, что она загружает ВСЕ регистры процессора, и может выполняться в реальном режиме. Изменяя поле базы регистра кэша дескриптора (внутренний системный регистр процессора) программа может обратиться к сегменту, лежащему за пределами первого мегабайта адресного пространства.
Как мы уже говорили, команда LOADALL не имеет операндов. Регистры загружаются из буфера, который имеет длину 102 байта и должен быть подготовлен в области памяти с физическим адресом 00800h.
Формат буфера представлен в следующей таблице:
Таблица 16. Формат буфера для команды LOADALL.
Адрес |
Регистры процессора |
800h-805h |
Не используется |
806h-807h |
Слово состояния процессора MSW (Machine Status
Word) |
808h-815h |
Не используется |
816h-817h |
Регистр задачи TR (Task Register) |
818h-819h |
Регистр флагов |
81Ah-81Bh |
Регистр IP (Instruction Pointer) |
81Ch-81Dh |
Селектор LDT (Local Descriptor Table) |
81Eh-81Fh |
Регистр DS (Data Segment Selector) |
820h-821h |
Регистр SS (Stack Segment Selector) |
822h-823h |
Регистр CS (Code Segment Selector) |
824h-825h |
Регистр ES (Extra Segment Selector) |
826h-827h |
Регистр DI (Destination Index) |
818h-829h |
Регистр SI (Source Index) |
82Ah-82Bh |
Регистр BP (Base Pointer) |
82Ch-82Dh |
Регистр SP (Stack Pointer) |
82Eh-82Fh |
Регистр BX (Data Register BX) |
830h-831h |
Регистр DX (Data Register DX) |
832h-833h |
Регистр CX (Data Register CX) |
834h-835h |
Регистр AX (Accumulator) |
836h-83Bh |
Кэш дескриптора ES |
83Ch-841h |
Кэш дескриптора CS |
842h-847h |
Кэш дескриптора SS |
848h-84Dh |
Кеш дескриптора DS |
84Eh-853h |
Регистр GDTR (Global Descriptor Table Register) |
854h-859h |
Кэш дескриптора LDT |
85Ah-85Fh |
Регистр IDTR (Interrupt Descriptor Table Register) |
860h-865h |
Кэш дескриптора TSS (Task State Segment) |
Для ускорения доступа к содержимому дескрипторных таблиц в процессоре имеются так называемые теневые регистры или регистры кэша дескрипторов. Когда процессор загружает селектор в сегментный регистр, автоматически выполняется загрузка соответствующего регистра кэша дескриптора. Не существует какого-либо иного способа загрузить кэш дескриптора явно из программы с помощью обычных команд. Однако вы можете воспользоваться для этого командой LOADALL, подготовив в описанном выше буфере необходимые значения.
Формат кэша дескриптора приведён в следующей
таблице:
Таблица 17. Формат кэша дескриптора.
Смещение поля |
Назначение поля |
0-2 |
24-битовый базовый адрес сегмента |
3 |
Байт доступа, его формат полностью
аналогичен формату байта доступа дескриптора, за
исключением бита присутствия. На месте этого
бита находится бит VALID. Если этот бит сброшен в 0,
при попытке использовать дескриптор для
адресации памяти произойдёт исключение 13 с кодом
ошибки 0. |
4-5 |
16-битовый предел сегмента |
Можно предложить следующий алгоритм использования команды LOADALL:
При выполнении команды LOADALL не делается никаких проверок. Вам необходимо самим позаботиться о том, чтобы загружаемые в регистры процессора значения имели какой-нибудь смысл. В противном случае состояние процессора окажется непредсказуемым.
Команда LOADALL может выполняться в защищённом режиме в нулевом приоритетном кольце. Но, к сожалению, эту команду нельзя использовать для переключения процессора из защищённого в реальный режим.
Процессор i80387 также имеет команду LOADALL, но её код и выполняемые функции другие.
Для определения активных интерфейсов с защищённым режимом можно использовать предлагаемую утилиту MEMOSCOP. Эта утилита проверяет присутствие всех уровней поддержки программ, работающих в защищённом режиме или с расширенной памятью - от BIOS до DPMI.
*MEMOSCOP*, © Frolov A.V., 1992 -------------------------------------------------------- Файл memoscop.c #include <stdio.h> #include <stdlib.h> #include <dos.h> void main(void) { extern int getcpu(void); unsigned cpu_type, ver; unsigned err; char ver_hi, ver_lo, verems; unsigned hostdata_seg, hostdata_size, dpmi_flags; void (far *pm_entry)(); union REGS regs; struct SREGS segregs; printf("\n*MemoScop* v 1.0, © Frolov A.V., 1992\n" "---------------------------------------\n"); // Определяем тип центрального процессора. Если программа // работает на процессоре i8086, завершаем выполнение, // так как в этом случае интерфейсы с защищенным режимом // недоступны. printf("Тип процессора: 80%d\n", (cpu_type = getcpu())); if(cpu_type == 86) { printf("\nНа этом процессоре нам работать не интересно..."); exit(0); } // Определяем размер доступной через прерывание INT 15h // расширенной памяти. printf("\n------------------------ Уровень BIOS -------------------------\n"); regs.h.ah = 0x88; int86(0x15, ®s, ®s); printf("Размер расширенной памяти, доступной через INT 15:" " \t%d Кбайт\n", regs.x.ax); // Проверяем, установлен ли драйвер HIMEM.SYS, // если установлен, выводим его версию. printf("\n------------------------ Уровень XMM --------------------------\n"); if (XMM_Installed()) { printf("Установлен драйвер HIMEM.SYS"); ver = XMM_Version(); printf(", версия: %4X, изменения: %4X\n", (short)ver, (short)(ver >> 16)); printf("Размер свободной расширенной памяти, доступной через XMM:" " %ld Кбайт\n", (long)XMM_QueryLargestFree()); printf("Общий размер расширенной памяти, доступной через XMM:" " \t %ld Кбайт\n", (long)XMM_QueryTotalFree()); } else printf("\nДрайвер HIMEM.SYS не установлен."); printf("\n------------------------ Уровень EMS/VCPI ----------------------\n"); // Проверяем наличие драйвера EMS/VCPI if(ems_init()) printf("Драйвер EMS/VCPI не загружен\n"); else { printf("Драйвер EMS/VCPI загружен, "); // Выводим номер версии драйвера if((err = ems_ver(&verems)) != 0) { printf("\nОшибка %02.2X при определении версии EMM", err); exit(-1); } printf("версия EMM: %02.2X", verems); // Определяем присутствие VCPI и его версию if(vcpi_ver(&ver_hi, &ver_lo) != 0) { printf("\nДрайвер EMM не поддерживает VCPI\n"); exit(-1); } printf("\nВерсия VCPI: %02.2X.%02.2X\n", ver_hi, ver_lo); } printf("\n------------------------ Уровень DPMI --------------------------"); // Проверяем доступность и параметры сервера DPMI regs.x.ax = 0x1687; int86x(0x2F, ®s, ®s, &segregs); if(regs.x.ax != 0) { printf("\nСервер DPMI не активен"); exit(-1); } // Определяем версию сервера DPMI printf("\nВерсия сервера DPMI: \t\t\t%d.%d\n", regs.h.dh, regs.h.dl); // Определяем тип процессора printf("Тип процессора:\t\t\t\t"); if(regs.h.cl == 2) printf("80286"); else if(regs.h.cl == 3) printf("80386"); else if(regs.h.cl == 4) printf("80486"); // Определяем возможность работы с 32-разрядными // программами dpmi_flags = regs.x.bx; printf("\nПоддержка 32-разрядных программ:\t"); if(dpmi_flags && 1) printf("ПРИСУТСТВУЕТ"); else printf("ОТСУТСТВУЕТ"); // Определяем размер области памяти для сервера DPMI hostdata_size = regs.x.si; printf("\nРазмер памяти для сервера DPMI:\t\t%d байт", hostdata_size * 16); // Определяем адрес точки входа в защищённый режим FP_SEG(pm_entry) = segregs.es; FP_OFF(pm_entry) = regs.x.di; printf("\nАдрес точки входа в защищённый режим: \t%Fp\n", pm_entry); getch(); } /** *.Name ems_init *.Title Функция проверяет установку драйвера EMS * *.Descr Эта функция проверяет наличие драйвера EMS * *.Proto int ems_init(void); * *.Params Не используются * *.Return 0 - драйвер EMS установлен; * 1 - драйвер EMS не установлен. * *.Sample ems_test.c **/ int ems_init(void) { void (_interrupt _far *EMS_driver_adr)(void); char _far *EMS_driver_name; char test_name[8]; int i; EMS_driver_adr = _dos_getvect(0x67); FP_SEG(EMS_driver_name) = FP_SEG (EMS_driver_adr); FP_OFF(EMS_driver_name) = 10; for(i=0; i<8; i++) test_name[i] = EMS_driver_name[i]; if(strncmp(test_name, "EMMXXXX0", 8) == 0) return(0); else return(1); } /** *.Name ems_ver *.Title Определение версии драйвера EMS * *.Descr Эта функция возвращает номер версии * драйвера EMS в двоично-десятичном формате. * *.Proto int ems_ver(char *ver); * *.Params char *ver - указатель на байт, в который * будет записан номер версии. * *.Return Номер версии драйвера EMS в формате BCD * *.Sample ems_test.c **/ int ems_ver(char *ver) { union REGS reg; reg.x.ax = 0x4600; int86(0x67, ®, ®); *ver = reg.h.al; return(reg.h.ah); } int vcpi_ver(char *ver_hi, char *ver_lo) { union REGS reg; reg.x.ax = 0xDE00; int86(0x67, ®, ®); *ver_hi = reg.h.bh; *ver_lo = reg.h.bl; return(reg.h.ah); }
Исходные тексты функций, вызываемых утилитой MEMOSCOP приведены ниже:
*MEMOSCOP*, © Frolov A.V., 1992 -------------------------------------------------------- Файл cpu.asm DEAL MODEL SMALL P386 PUBLIC _getcpu CODESEG PROC _getcpu NEAR ; -------------------------------------------------------- ; Пытаемся установить старшую тетраду регистра флагов в 0. ; Если программа работает на процессоре 8086, в этом ; байте все биты будут установлены в 1 ; -------------------------------------------------------- mov dx, 86 ; Тип процессора - 8086 pushf pop bx and bh,0Fh push bx popf pushf pop ax and ah,0F0h cmp ah,0F0h je cpu_end ; -------------------------------------------------------- ; Для теста на процессор 80286 пытаемся установить старшую ; тетраду регистра флагов в единицы. Если программа ; выполняется на процессоре 80286, эти биты останутся ; равными нулю ; -------------------------------------------------------- mov dx, 286 ; Тип процессора - 80286 or bh,0F0h push bx popf pushf pop ax test ah,0F0h jz cpu_end ; -------------------------------------------------------- ; Для того, чтобы отличить процессор 80386 от процессора ; 80486, используем бит 18 регистра флагов EFLAGS. ; Если программа может изменить состояние этого бита, ; она выполняется на процессоре 80486. В противном случае ; используется процессор 80386. ; -------------------------------------------------------- ; Созраняем указатель стека mov edx,esp ; Выравниваем указатель стека для предотвращения ; исключения при установке флага AC and esp,not 3 ; Копируем регистр EFLAGS в регистр EAX pushfd pop eax ; Сохраняем начальное значение регистра EFLAGS mov ecx,eax ; Переключаем флаг AC xor eax,40000H ; Пытаемся записать измененное значение обратно в регистр ; EFLAGS push eax popfd ; Копируем регистр EFLAGS в регисрр EAX pushfd pop eax ; Сравниваем старое и новое значения бита AC xor eax,ecx shr eax,18 and eax,1 push ecx ; Восстанавливаем регситр EFLAGS popfd ; Восстанавливаем указатель стека mov esp,edx ; Теперь если программа выполняется на процессоре 80386, ; регистр AX содержит значение 0, а если на процессоре ; 80486 - значение 1. mov dx,386 ; Тип процессора - 80386 test ax,ax jz cpu_end mov ax,486 ; Тип процессора - 80486 cpu_end: mov ax, dx ret ENDP _getcpu END
Для работы с функциями драйвера HIMEM.SYS используется интерфейс, описанный нами в томе 2 "Библиотеки системного программсита":
*MEMOSCOP*, © Frolov A.V., 1992 -------------------------------------------------------- Файл xmmc.asm ; Это интерфейсный модуль для вызова функций ; XMS из Си. Текст программы рассчитан на ; модель памяти Small. .model small,c .DATA ; В этом месте будет храниться адрес ; управляющей функции XMM XMM_Control dd ? .CODE ; Макроопределения для выполнения соглашения об ; использовании регистров в процедурах Си c_begin macro push bp mov bp,sp push si push di endm c_end macro pop di pop si mov sp,bp pop bp ret endm ; Все процедуры должны быть public public XMM_Installed public XMM_Version public XMM_RequestHMA public XMM_ReleaseHMA public XMM_GlobalEnableA20 public XMM_GlobalDisableA20 public XMM_EnableA20 public XMM_DisableA20 public XMM_QueryA20 public XMM_QueryLargestFree public XMM_QueryTotalFree public XMM_AllocateExtended public XMM_FreeExtended public XMM_MoveExtended public XMM_LockExtended public XMM_UnLockExtended public XMM_GetHandleLength public XMM_GetHandleInfo public XMM_ReallocateExtended public XMM_RequestUMB public XMM_ReleaseUMB ;** ;.Name XMM_Installed ;.Title Получение адреса управляющей функции ; ;.Descr Эта функция проверяет наличие драйвера ; HIMEM.SYS и в случае его присуствия ; запоминает адрес управляющей функции. ; ;.Proto unsigned XMM_Installed(void); ; ;.Params Не используются ; ;.Return 0 - драйвер HIMEM.SYS не установлен; ; 1 - драйвер HIMEM.SYS установлен. ; ;.Sample xms_test.c ;** XMM_Installed proc near c_begin mov ax, 4300h int 2fh cmp al, 80h jne NotInstalled mov ax, 4310h int 2fh mov word ptr [XMM_Control], bx mov word ptr [XMM_Control+2], es mov ax,1 jmp Installed NotInstalled: mov ax, 0 Installed: c_end XMM_Installed endp ;** ;.Name XMM_Version ;.Title Определение версии драйвера HIMEM.SYS ; ;.Descr Эта функция определяет версию драйвера ; HIMEM.SYS ; ;.Proto long XMM_Version(void); ; ;.Params Не используются ; ;.Return Номер версии в младших 16 битах, ; номер изменений - в старших 16 битах ; возвращаемого значения ; ;.Sample xms_test.c ;** XMM_Version proc near push si push di xor ah,ah call [XMM_Control] mov dx, bx pop di pop si ret XMM_Version endp ;** ;.Name XMM_RequestHMA ;.Title Запросить область HMA ; ;.Descr Эта функция пытается зарезервировать для ; программы область HMA ; ;.Proto long XMM_RequestHMA(unsigned space); ; ;.Params space - размер требуемой области для ; TSR-программы или драйвера, ; 0xffff для прикладной программы; ; ;.Return < 0 - область HMA не назначена программе, ; код ошибки находится в старшем байте. ; 0L - область HMA назначена программе. ; ;.Sample xms_test.c ;** XMM_RequestHMA proc near c_begin mov ah, 1 mov dx, [bp+4] call [XMM_Control] xor dx, dx dec ax jz @success mov dh, bl @success: c_end XMM_RequestHMA endp ;** ;.Name XMM_ReleaseHMA ;.Title Освободить область HMA ; ;.Descr Эта функция пытается освободить ; область HMA ; ;.Proto long XMM_ReleaseHMA(void); ; ;.Params Не используются ; ;.Return < 0 - область HMA не освобождена, ; код ошибки находится в старшем байте. ; 0L - область HMA освобождена. ; ;.Sample xms_test.c ;** XMM_ReleaseHMA proc near c_begin mov ah, 2 call [XMM_Control] xor dx, dx dec ax jz @success1 mov dh, bl @success1: c_end XMM_ReleaseHMA endp ;** ;.Name XMM_GlobalEnableA20 ;.Title Глобальное разрешение линии A20 ; ;.Descr Эта функция разрешает программе, получившей ; доступ к области HMA использовать линию A20 ; ;.Proto long XMM_GlobalEnableA20(void); ; ;.Params Не используются ; ;.Return < 0 - линия A20 не включена, ; код ошибки находится в старшем байте. ; 0L - линия A20 включена. ; ;.Sample xms_test.c ;** XMM_GlobalEnableA20 proc near c_begin mov ah, 3 call [XMM_Control] xor dx, dx dec ax jz @success2 mov dh, bl @success2: c_end XMM_GlobalEnableA20 endp ;** ;.Name XMM_GlobalDisableA20 ;.Title Глобальное запрещение линии A20 ; ;.Descr Эта функция запрещает программе, получившей ; доступ к области HMA использовать линию A20 ; ;.Proto long XMM_GlobalDisableA20(void); ; ;.Params Не используются ; ;.Return < 0 - линия A20 не выключена, ; код ошибки находится в старшем байте. ; 0L - линия A20 выключена. ; ;.Sample xms_test.c ;** XMM_GlobalDisableA20 proc near c_begin mov ah, 4 call [XMM_Control] xor dx, dx dec ax jz @success3 mov dh, bl @success3: c_end XMM_GlobalDisableA20 endp ;** ;.Name XMM_EnableA20 ;.Title Локальное разрешение линии A20 ; ;.Descr Эта функция разрешает программе управлять ; областью расширенной памяти. ; ;.Proto long XMM_EnableA20(void); ; ;.Params Не используются ; ;.Return < 0 - линия A20 не включена, ; код ошибки находится в старшем байте. ; 0L - линия A20 включена. ; ;.Sample xms_test.c ;** XMM_EnableA20 proc near c_begin mov ah, 5 call [XMM_Control] xor dx, dx dec ax jz @success4 mov dh, bl @success4: c_end XMM_EnableA20 endp ;** ;.Name XMM_DisableA20 ;.Title Локальное запрещение линии A20 ; ;.Descr Эта функция запрещает программе управлять ; областью расширенной памяти. ; ;.Proto long XMM_DisableA20(void); ; ;.Params Не используются ; ;.Return < 0 - линия A20 не выключена, ; код ошибки находится в старшем байте. ; 0L - линия A20 выключена. ; ;.Sample xms_test.c ;** XMM_DisableA20 proc near c_begin mov ah, 6 call [XMM_Control] xor dx, dx dec ax jz @success5 mov dh, bl @success5: c_end XMM_DisableA20 endp ;** ;.Name XMM_QueryA20 ;.Title Проверить состояние линии A20 ; ;.Descr Эта функция проверяет доступность ; линии A20 ; ;.Proto long XMM_QueryA20(void); ; ;.Params Не используются ; ;.Return < 0 - ошибка, ; код ошибки находится в старшем байте. ; 0L - линия A20 выключена, ; 1L - линия A20 включена. ; ;.Sample xms_test.c ;** XMM_QueryA20 proc near c_begin mov ah, 7 call [XMM_Control] xor dx, dx or ax, ax jnz @success6 mov dh, bl @success6: c_end XMM_QueryA20 endp ;** ;.Name XMM_QueryLargestFree ;.Title Определить максимальный размер блока ; ;.Descr Эта функция возвращает размер максимального ; непрерывного блока расширенной памяти, ; который доступен программе. ; ;.Proto long XMM_QueryLargestFree(void); ; ;.Params Не используются ; ;.Return < 0 - ошибка, ; код ошибки находится в старшем байте. ; >= 0 - размер блока. ; ;.Sample xms_test.c ;** XMM_QueryLargestFree proc near c_begin mov ah, 8 call [XMM_Control] xor dx, dx or ax, ax jnz @success7 mov dh, bl @success7: c_end XMM_QueryLargestFree endp ;** ;.Name XMM_QueryTotalFree ;.Title Определить размер расширенной памяти ; ;.Descr Эта функция возвращает размер ; всей имеющейся расширенной памяти. ; ;.Proto long XMM_QueryTotalFree(void); ; ;.Params Не используются ; ;.Return < 0 - ошибка, ; код ошибки находится в старшем байте. ; >= 0 - размер расширенной памяти. ; ;.Sample xms_test.c ;** XMM_QueryTotalFree proc near c_begin mov ah, 8 call [XMM_Control] or ax, ax mov ax, dx mov dx, 0 jnz @success8 mov dh, bl @success8: c_end XMM_QueryTotalFree endp ;** ;.Name XMM_AllocateExtended ;.Title Запросить блок расширенной памяти ; ;.Descr Эта функция выделяет программе блок ; расширенной памяти, в случае успеха ; возвращает индекс полученного блока. ; ;.Proto long XMM_AllocateExtended(unsigned space); ; ;.Params space - размер требуемого блока памяти ; в килобайтах; ; ;.Return < 0 - блок не распределен, ; код ошибки находится в старшем байте. ; > 0L - младший байт содержит индекс ; полученного блока памяти. ; ;.Sample xms_test.c ;** XMM_AllocateExtended proc near c_begin mov ah, 9 mov dx, [bp+4] call [XMM_Control] or ax, ax mov ax, dx mov dx, 0 jnz @success9 mov dh, bl @success9: c_end XMM_AllocateExtended endp ;** ;.Name XMM_FreeExtended ;.Title Освободить блок расширенной памяти ; ;.Descr Эта функция освобождает блок ; расширенной памяти, полученный функцией ; XMM_AllocateExtended(). ; ;.Proto long XMM_FreeExtended(unsigned handle); ; ;.Params handle - индекс освобождаемого блока памяти; ; ;.Return < 0 - блок не распределен, ; код ошибки находится в старшем байте. ; 0L - блок освобожден. ; ;.Sample xms_test.c ;** XMM_FreeExtended proc near c_begin mov ah, 0Ah mov dx, [bp+4] call [XMM_Control] xor dx, dx dec ax jz @successA mov dh, bl @successA: c_end XMM_FreeExtended endp ;** ;.Name XMM_MoveExtended ;.Title Копировать блок расширенной памяти ; ;.Descr Эта функция копирует блок ; расширенной памяти, используя структуру ; struct XMM_Move: ; ; struct XMM_Move { ; unsigned long Length; ; unsigned short SourceHandle; ; unsigned long SourceOffset; ; unsigned short DestHandle; ; unsigned long DestOffset; ; }; ; ;.Proto long XMM_MoveExtended(struct ; XMM_Move *move_descr); ; ;.Params struct XMM_Move *move_descr - ; указатель на структуру, описывающую ; что, откуда и куда надо копировать. ; ;.Return < 0 - ошибка при копировании, ; код ошибки находится в старшем байте. ; 0L - блок скопирован успешно. ; ;.Sample xms_test.c ;** XMM_MoveExtended proc near c_begin mov ah, 0Bh mov si, [bp+4]; call [XMM_Control] xor dx, dx dec ax jz @successB mov dh, bl @successB: c_end XMM_MoveExtended endp ;** ;.Name XMM_LockExtended ;.Title Заблокировать блок расширенной памяти ; ;.Descr Эта функция блокирует блок расширенной ; памяти и возвращает 31 разряд его ; физического адреса. ; ;.Proto long XMM_LockExtended(unsigned handle); ; ;.Params handle - индекс блокируемого блока памяти; ; ;.Return < 0 - блок не заблокирован, ; код ошибки находится в старшем байте. ; > 0L - блок заблокирован, функция ; возвращает физический адрес блока ; памяти. ; ;.Sample xms_test.c ;** XMM_LockExtended proc near c_begin mov ah, 0Ch mov dx, [bp+4] call [XMM_Control] xchg ax, bx dec bx jz XMML_Success mov dh, al XMML_Success: c_end XMM_LockExtended endp ;** ;.Name XMM_UnLockExtended ;.Title Разблокировать блок расширенной памяти ; ;.Descr Эта функция разблокирует блок расширенной ; памяти. ; ;.Proto long XMM_UnLockExtended(unsigned handle); ; ;.Params handle - индекс блока памяти; ; ;.Return < 0 - блок не разблокирован, ; код ошибки находится в старшем байте. ; 0L - блок разблокирован. ; ;.Sample xms_test.c ;** XMM_UnLockExtended proc near c_begin mov ah, 0Dh mov dx, [bp+4] call [XMM_Control] xor dx, dx dec ax jz @successC mov dh, bl @successC: c_end XMM_UnLockExtended endp ;** ;.Name XMM_GetHandleLength ;.Title Получить длину блока расширенной памяти ; ;.Descr Эта функция возвращает длину блока ; расширенной памяти по его индексу. ; ;.Proto long XMM_GetHandleLength(unsigned handle); ; ;.Params handle - индекс блока памяти; ; ;.Return < 0 - произошла ошибка, ; код ошибки находится в старшем байте. ; > 0L - длина блока в килобайтах. ; ;.Sample xms_test.c ;** XMM_GetHandleLength proc near c_begin mov ah, 0Eh mov dx, [bp+4] call [XMM_Control] or ax, ax mov ax, dx mov dx, 0 jnz @successD mov dh, bl @successD: c_end XMM_GetHandleLength endp ;** ;.Name XMM_GetHandleInfo ;.Title Получить информацию о блоке расширенной памяти ; ;.Descr Эта функция возвращает общее ; количество индексов в системе и ; содержимое счетчика блокирования для ; заданного индекса. ; ;.Proto long XMM_GetHandleInfo(unsigned handle); ; ;.Params handle - индекс блока памяти; ; ;.Return < 0 - произошла ошибка, ; код ошибки находится в старшем байте. ; > 0L - младший байт - общее количество ; индексов в системе; ; старший байт - счетчик блокирования. ; ;.Sample xms_test.c ;** XMM_GetHandleInfo proc near c_begin mov ah, 0Eh mov dx, [bp+4] call [XMM_Control] mov dx, bx or ax, ax mov ax, dx mov dx, 0 jnz @successE mov dh, bl @successE: c_end XMM_GetHandleInfo endp ;** ;.Name XMM_ReallocateExtended ;.Title Изменить размер блока расширенной памяти ; ;.Descr Эта функция изменяет размер выделенного ; блока расширенной памяти. ; ;.Proto long XMM_ReallocateExtended(unsigned handle, ; unsigned new_size); ; ;.Params handle - индекс блока памяти; ; new_size - новый размер блока памяти ; в килобайтах; ; ;.Return < 0 - блок не распределен, ; код ошибки находится в старшем байте. ; > 0L - младший байт содержит индекс ; полученного блока памяти. ; ;.Sample xms_test.c ;** XMM_ReallocateExtended proc near c_begin mov ah, 0Fh mov dx, [bp+4] mov bx, [bp+6] call [XMM_Control] xor dx, dx dec ax jz @successF mov dh, bl @successF: c_end XMM_ReallocateExtended endp ;** ;.Name XMM_RequestUMB ;.Title Запросить область UMB ; ;.Descr Эта функция пытается зарезервировать для ; программы область UMB ; ;.Proto long XMM_RequestUMB(unsigned space); ; ;.Params space - размер требуемой области ; в параграфах; ; ;.Return < 0 - область UMB не назначена программе, ; код ошибки находится в старшем байте; ; максимальный размер доступного блока ; в младшем слове (16 разрядов); ; > 0L - область UMB назначена программе, ; младшее слово содержит сегмент блока ; UMB, старший - размер выделенного ; блока UMB. ; ;.Sample xms_test.c ;** XMM_RequestUMB proc near c_begin mov ah, 10h mov dx, [bp+4] call [XMM_Control] xchg bx, ax dec bx jz RUMB_Success xchg ax, dx mov dh, dl RUMB_Success: c_end XMM_RequestUMB endp ;** ;.Name XMM_ReleaseUMB ;.Title Освободить область UMB ; ;.Descr Эта функция пытается освободить ; область UMB ; ;.Proto long XMM_ReleaseUMB(unsigned segment); ; ;.Params segment - сегмент освобождаемого блока UMB* ; ;.Return < 0 - область UMB не освобождена, ; код ошибки находится в старшем байте. ; 0L - область UMB освобождена. ; ;.Sample xms_test.c ;** XMM_ReleaseUMB proc near c_begin mov ah, 11h mov dx, [bp+4] call [XMM_Control] xor dx, dx dec ax jz @success10 mov dh, bl @success10: c_end XMM_ReleaseUMB endp END
Если вы разрабатываете программное обеспечение, защищённое от несанкционированного копирования, вам необходимо позаботиться о том, чтобы потенциальные взломщики ("кракеры" и "хакеры") не смогли выполнить программу инсталляции под управлением отладчика. Если взломщик сможет "подглядеть" за работой вашей программы, он рано или поздно разгадает ваш замысел и сведёт на нет все ваши усилия по защите программы от копирования.
В третьей книге первого тома "Библиотеки системного программиста" мы излагали некоторые соображения по организации защиты программ от несанкционированного копирования. Мы, в частности, рассказали о некоторых методах защиты программ от отладки - использование таймера, внутренней очереди команд процессора и другие.
Защищённый режим работы процессора открывает перед вами новую возможность. Возьмите любую программу, приведённую в этой книге и попытайтесь запустить её под управлением какого-либо отладчика (например, попробуйте Turbo Debugger или Code View). Всё будет хорошо до тех пор, пока ваша программа не попытается загрузить регистр IDTR при помощи команды LIDT. После выполнения этой команды отладчик зависает и единственное средство вновь оживить компьютер - нажать на кнопку сброса, расположенную на системном блоке.
Причина очевидна - изменились расположение и формат дескрипторной таблицы прерываний. Она подготовлена для работы в защищённом режиме, но отладчик работает в реальном режиме. Поэтому обработка всех прерываний, в том числе и от клавиатуры, невозможна.
Идея использования защищённого режима работы процессора при создании программ, защищённых от несанкционированного копирования, очевидна. Используя примеры программ, приведённые в книге, вы сможете во время работы программы инсталляции перевести процессор в защищённый режим и выполнить часть работы по инсталляции в защищённом режиме.
Например, перед переключением в защищённый режим вы можете подготовить в памяти массив контрольной информации. Расшифровка и проверка этого массива, а также запись данных в нестандартные сектора инсталляционной дискеты могут выполняться в защищённом режиме. При этом, пользуясь обычными отладчиками, невозможно определить действия, выполняемые в защищённом режиме. Особенно, если участок программы, работающий в защищённом режиме, зашифрован.
Далее процессор можно вернуть в реальный режим и продолжить процесс инсталляции.
Находясь в защищённом режиме, вы можете читать и писать сектора дискеты только используя уровень портов ввода/вывода контроллера флоппи-диска. Программирование контроллера флоппи-диска описано в третьей книге первого тома "Библиотеки системного программиста". Обрабатывать прерывания в защищённом режиме вы уже умеете.
Очевидный недостаток применения защищённого режима при организации защиты от копирования заключается в необходимости использования процессоров i80286, i80386 или i80486. Это означает, что указанный метод непригоден для компьютеров IBM PC/XT, использующих процессор i8086 или i8088.
Однако в последнее время большинство
программных комплексов требует наличия в
компьютере по крайней мере процессора i80286,
поэтому указанным недостатком можно пренебречь.
К тому же следует учесть резкое увеличение
стойкости инсталлятора, работающего в
защищённом режиме, к попыткам взлома.