Аппаратное обеспечение IBM PC

         

Блокирование EMB


На входе: AH = 0Ch;

DX = 16-битовый индекс (handle) блокируемого EMB.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка;

DX:BX = 32-битовый линейный адрес заблокированного EMB.

Ошибки: BL = 80h, 81h, A2h, ACh, ADh.

Функция блокирует EMB и возвращает его базовый адрес как линейный 32-разрядный адрес. Для заблокированного EMB невозможно выполнить операцию копирования. Полученный линейный адрес действителен только для заблокированного EMB.



если функция выполнена успешно 0000h


На входе: AH = 03h.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, 82h.

Эта функция предназначена для тех программ, которые будут использовать область HMA. Она разрешает работу заблокированной по умолчанию 21-ой адресной линии процессора. Перед возвратом управления системе программа должна закрыть линию A20 с помощью функции04h.

Следует отметить, что на многих типах компьютеров переключение линии A20 - достаточно медленная операция.


если функция выполнена успешно 0000h


На входе: AH = 04h.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, 82h, 94h.

Функция предназначена для тех программ, которые используют область HMA. Она должна выполняться перед завершением работы такой программы.




Интерфейс с Си


Приведем текст программы, позволяющей программам, составленным на языке программирования Си, использовать функции драйвера расширенной памяти. Эта программа будет работать только в моделях памяти Small и Compact. Для других моделей памяти требуется изменить строки программы, в которых передаваемые функциям параметры извлекаются из стека и тип процедур (FAR):

Аргументы Small, Compact Large, Huge

Первый аргумент [bp+4] [bp+6] Второй аргумент [bp+6] [bp+8]

; Это интерфейсный модуль для вызова функций ; 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

Приведем пример программы, демонстрирующей использование некоторых функций XMM:

#include <stdio.h> #include <conio.h> #include "sysp.h"

void main(void); void main(void) {

long ver, rc, handle; static char testmsg[] = "Тестовое сообщение"; char buf[80]; char far *ptr; int i; struct XMM_Move move_d;

// Проверяем, установлен ли драйвер HIMEM.SYS, // если установлен, выводим его версию.

if (XMM_Installed()) { printf("\nДрайвер HIMEM.SYS установлен."); ver = XMM_Version(); printf("\nВерсия XMM: %4X, изменения: %4x", (short)ver, (short)(ver >> 16)); }

else { printf("\nДрайвер HIMEM.SYS не установлен."); exit(-1); }

// Запрашиваем управление областью HMA.

rc = XMM_RequestHMA(0xffff); if(rc) error("Ошибка при запросе области HMA",rc);

else {

// Открываем линию A20.

rc = XMM_GlobalEnableA20(); if(rc) error("Ошибка при разрешении линии A20",rc);

// Копируем тестовое сообщение сначала из // стандартной памяти в область HMA, // затем обратно в стандартную память.

ptr = FP_MAKE(0xffff,0x0010);

for(i=0; testmsg[i] != 0; i++) ptr[i] = testmsg[i];

for(i=0; ptr[i] != 0; i++) buf[i] = ptr[i]; buf[i] = 0;

// Выводим сообщение для проверки.

printf("\n%s",buf);

// Закрываем линию A20 и отдаем системе область HMA.

rc = XMM_GlobalDisableA20(); if(rc) error("Ошибка при запрещении линии A20",rc);

rc = XMM_ReleaseHMA(); if(rc) error("Ошибка при освобождении области HMA",rc); }

// Получаем блок EMB размером в 1 килобайт.

handle = XMM_AllocateExtended(1); if(handle < 0) error("Ошибка при запросе XMB",handle);

// Копируем тестовое сообщение сначала из // стандартной памяти в блок EMB, // затем обратно в стандартную память.

move_d.Length = strlen(testmsg) + 1; move_d.SourceHandle = 0; (char far*)move_d.SourceOffset = (char far*)testmsg; move_d.DestHandle = handle; move_d.DestOffset = 0L;

rc = XMM_MoveExtended(&move_d); if(rc < 0) error("Ошибка при копировании в EMB",rc);

move_d.Length = strlen(testmsg) + 1; move_d.DestHandle = 0; (char far*)move_d.DestOffset = (char far*)buf; move_d.SourceHandle = handle; move_d.SourceOffset = 0L;

rc = XMM_MoveExtended(&move_d); if(rc < 0) error("Ошибка при копировании из EMB",rc);

// Выводим сообщение для проверки.

printf("\n%s",buf);

// Освобождаем блок EMB.

rc = XMM_FreeExtended(handle); if(rc) error("Ошибка при освобождении XMB",rc);

exit(0); }

// Функция для вывода сообщения об ошибке // и кода ошибки.

int error(char *msg, long rc) {

rc = (unsigned char)(rc >> 24) ; printf("\n%s, код ошибки: %02.2X\n", msg, (unsigned char)rc);

}


Изменить размер EMB


На входе: AH = 0Fh;

DX = 16-битовый индекс (handle) незаблокированного EMB, размер которого должен быть изменен;

BX = новый размер EMB в килобайтах.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, A0h, A1h, A2h, ABh.

Функция изменяет размер незаблокированного EMB. Если блок уменьшается в размерах, данные в старших адресах блока будут потеряны.



Коды ошибок


Приведем таблицу кодов ошибок, возвращаемых функциями в регистре BL:

Код Ошибка
00h Нет ошибки, нормальное завершение
80h Функция не реализована в текущей версии драйвера
81h Обнаружен драйвер VDISK.SYS, с этим драйвером драйвер HIMEM.SYS несовместим
82h Ошибка при работе с линией A20
8Eh Общая ошибка драйвера
8Fh Катастрофическая ошибка драйвера
90h Область HMA не существует
91h Область HMA уже используется
92h Содержимое регитра DX меньше парметра /HMAMIN=
93h Область HMA не распределена программе
94h Линия A20 все еще разблокирована
A0h Вся расширенная память уже распределена
A1h Больше нет свободных индексов EMB
A2h Неправильный индекс EMB
A3h Неправильный SourceHandle
A4h Неправильный SourceOffset
A5h Неправильный DestHandle
A6h Неправильный DestOffset
A7h Неправильный Length
A8h Неразрешенное перекрытие данных при выполнении операции пересылки данных
A9h Произошла ошибка четности
AAh EMB не заблокирован
ABh EMB заблокирован
ACh Переполнение счетчика блокировок EMB
ADh Не удалось выполнить блокировку EMB
B0h Доступен UMB меньшего размера
B1h Нет доступных блоков UMB
B2h Задан неправильный сегмент UMB



Копирование блоков EMB


На входе: AH = 0Bh.

DS:SI = указатель на управляющую структуру, определяющую откуда, куда и как будет выполняться копирование.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, 82h, A3h, A4h, A5h, A6h, A7h, A8h, A9h.

Управляющая структура:

ExtMemMoveStruct struc

Length dd ? ; количество пересылаемых байтов

SourceHandle dw ? ; индекс исходного блока SourceOffset dd ? ; смещение в исходном блоке

DestHandle dw ? ; индекс блока-назначения DestOffset dd ? ; смещение в блоке-назначении

ExtMemMoveStruct ends

Эта функция выполняет основную операцию с блоками EMB - копирование данных. Данные могут пересылаться между обычной памятью и блоками EMB, между различными блоками EMB и даже внутри обычной памяти.

Поле Length управляющей структуры указывает количество пересылаемых байтов. Это количество должно быть четным.

Поля SourceHandle и DestHandle указывают, соответственно, индексы исходного и результирующего блоков EMB. Если в качестве индекса задано значение 0000h, это означает, что в качестве источника или приемника данных используется обычная память.

Поля SourceOffset и DestOffset указывают 32-битовое смещение в блоке EMB или адрес в обычной памяти. В последнем случае этот адрес имеет стандартный формат сегмент:смещение.

Функция копирования сама управляет линией A20, восстанавливая ее состояние после выполнения копирования. Поэтому программе не требуется управлять линией A20.

Во время выполнения копирования разрешены прерывания.



если функция выполнена успешно 0000h


На входе: AH = 05h.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, 82h.

Эта функция предназначена только для тех программ, которые непосредственно управляют расширенной памятью. Перед завершением работы программа должна закрыть линию A20 при помощи функции 06h.


если функция выполнена успешно 0000h


На входе: AH = 06h.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, 82h, 94h.

Функция отменяет разрешение линии A20, запрошенное предыдущей функцией. Она предназначена только для тех программ, которые непосредственно управляют расширенной памятью.


Ограничения при использовании области HMA


К сожалению, на программы, использующие область HMA, накладываются значительные ограничения. Они связаны с тем, что MS-DOS версий 4.01 и более ранних, а также BIOS не были рассчитаны на работу с адресами памяти выше границы 1 мегабайт. Приведем список этих ограничений.

Нельзя передавать MS-DOS FAR-указатели на данные, размещенные в области HMA. Это связано с тем, что функции MS-DOS проверяют правильность таких указателей.

Не рекомендуется использование области HMA для выполнения обмена данных с диском через прерывания MS-DOS, BIOS или другими способами.

Драйверы и резидентные программы, использующие область HMA, не должны держать линию A20 постоянно включенной, так как это может привести к неправильной работе программ, не рассчитанных на наличие и доступность этой области.

Вектора прерываний не должны указывать в область HMA. Это результат предыдущего ограничения.



Описание функций драйвера HIMEM.SYS


Все функции драйвера HIMEM.SYS могут быть разделены на следующие пять групп:

функции получения информации о драйвере (0h);

функции управления областью HMA (1h...2h);

функции управления линией A20 (3h...7h);

функции управления расширенной памятью (8h...Fh);

функции управления блоками UMB (10h...11h).

Приведем подробное описание этих функций в соответствии со спецификацией XMS версии 2.0.



Определение размера свободной расширенной памяти


На входе: AH = 08h.

На выходе: AX = размер наибольшего свободного блока расширенной памяти в килобайтах;

DX = общий размер свободной расширенной памяти в килобайтах.

Ошибки: BL = 80h, 81h, A0h.

При определении размера свободной расширенной памяти в возвращаемое значение не включается 64К области HMA, даже если эта область не используется программами.



если линия A20 открыта; 0000h


На входе: AH = 07h.

На выходе: AX = 0001h - если линия A20 открыта; 0000h - если линия A20 закрыта.

Ошибки: BL = 00h, 80h, 81h.

Функция выполняет попытку адресоваться за границу 1мегабайта памяти и проверяет, не происходит ли при этом обращение в начало памяти (т.е. "свертка памяти").


Основные понятия


При обсуждении спецификации XMS мы будем использовать следующие понятия и термины.

Расширенная память (Extended Memory) - это память, используемая в компьютерах с процессорами 80286, 80386, 80486, располагающаяся в адресном пространстве выше одного мегабайта.

Старшая область памяти (High Memory Area) HMA - это первые 64 килобайта расширенной памяти, начинающиеся с адреса FFFFh:0010h. Адрес конца области HMA - FFFFh:FFFFh. Следовательно, размер области составляет 64 килобайта без 16 байтов. Следует отметить, что эта область может адресоваться процессором в реальном режиме и поэтому может быть использована обычными программами, предназначенными для работы в среде MS-DOS.

Верхние блоки памяти (Upper Memory Blocks) UMB - блоки памяти на машинах, использующих процессор 8086. Эти блоки памяти располагаются между границей 640 килобайт и 1 мегабайт. Расположение и размер этих блоков могут сильно изменяться в зависимости от аппаратуры.

Расширенные блоки памяти (Extended Memory Blocks) EMB- блоки расширенной памяти, располагающиеся выше границы HMA.

Линия A20 - двадцать первая адресная линия процессора. Обычно эта линия заблокирована. Разблокировка линии открывает программам доступ к области HMA.

На рисунке схематично показано расположение различных перечисленных выше блоков памяти в адресном пространстве:

г==============================================¬ ¦ Расширенные блоки памяти EMB ¦ ¦ ¦ ¦----------------------------------------------¦ 1088K ¦ ¦ ¦ Старшая область памяти HMA ¦ ¦ ¦ ¦----------------------------------------------¦ 1024K ¦ ¦ ¦ Верхние блоки памяти UMB ¦ ¦ ¦ ¦----------------------------------------------¦ 640 K ¦ ¦ ¦ Обычная память, используемая MS-DOS ¦ ¦ ¦ L==============================================- 0 K



Освободить блок EMB


На входе: AH = 0Ah;

DX = 16-битовый индекс (handle) полученного блока EMB.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, A2h, B2h.

Функция освобождает блок EMB, заказанный предыдущей функцией. При этом все данные, находившиеся в блоке, будут потеряны.



Освободить область HMA


На входе: AH = 02h.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, 90h, 93h.

Программы, которые запрашивали область HMA, должны освободить ее с использованием этой функции. При этом данные, которые находились в этой области, будут потеряны.

После того, как программа освободила область HMA, эта область становится доступной другим программам.



Освободить область UMB


На входе: AH = 11h;

DX = сегмент освобождаемого UMB.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, B2h.

После освобождения блока EMB данные, которые там находились, будут потеряны.



Получение адреса управляющей программы


Для вызова драйвера программа должна получить адрес специальной управляющей программы, которая выполняет все функции по обслуживанию расширенной памяти и области HMA.

Этот адрес можно получить, если загрузить в регистр AX значение 4310h и вызвать прерывание INT 2Fh. Прерывание возвратит сегментный адрес управляющей программы в регистре ES, смещение - в регистре BX:

; Получаем адрес управляющей функции драйвера

mov ax, 4310h int 2fh mov word ptr cs:[HMMEntry][0], bx mov word ptr cs:[HMMEntry][2], es

В дальнейшем полученный адрес используется для выполнения функций по обслуживанию расширенной памяти. Перед вызовом управляющей программы код требуемой функции должен быть загружен в регистр AH:

; Получаем номер версии драйвера HIMEM.SYS

mov ax,0 call [HMMEntry]

Программы, которые обращаются к управляющей функции, должны перед вызовом функции иметь размер стека не менее 256 байтов.



Получить блок EMB


На входе: AH = 09h.

DX = размер требуемого блока в килобайтах.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка;

DX = 16-битовый индекс (handle) полученного блока EMB.

Ошибки: BL = 80h, 81h, A0h, A1h.

Функция заказывает блок EMB из пула свободной расширенной памяти. При успешном выполнении запроса функция возвращает индекс полученного блока, который должен использоваться программой для выполнения всех операций с блоком EMB. Если блок EMB программе больше не нужен, она должна освободить его с помощью функции 0Ah.

Количество блоков EMB, которое может быть заказано, определяется в командной строке драйвера HIMEM.SYS параметром /NUMHANDLES=. Значение по умолчанию - 32, максимальное значение - 128.



Получить информацию об индексе EMB


На входе: AH = 0Eh;

DX = 16-битовый индекс (handle) EMB.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка;

BH = содержимое счетчика блокировок EMB;

BL = количество свободных индексов EMB в системе;

DX = размер блока в килобайтах.

Ошибки: BL = 80h, 81h, A2h.

Эта функция используется для получения различной информации об используемых блоках EMB. Линейный адрес блока может быть получен с помощью фунции 0Ch.



Получить версию XMS


На входе: AH = 00h.

На выходе: AX = номер версии XMS;

BX = номер внутренней модификации драйвера;

DX = 0001h - если существует область HMA, 0000h - если область HMA не существует.

Ошибки: нет.

Функция возвращает номера версии и модификации XMS в двоично-десятичном (BCD) формате. Например, если AX=0250h, это означает, что драйвер реализует спецификацию XMS версии 2.50. Дополнительно функция позволяет проверить наличие в системе области HMA.



Примеры программ


Первая программа демонстрирует проверку подключения драйвера и использование его основных функций:

include sysp.inc

.MODEL tiny DOSSEG

.STACK 100h

.DATA

msg DB 13,10,"Работа с драйвером HIMEM.SYS", 13, 10 DB "Copyright ©Frolov A.,1991",13,10,13,10 DB "$"

noHMM DB 13,10 DB "Драйвер HIMEM.SYS не установлен",13,10,"$" yesHMM DB 13,10,"Драйвер HIMEM.SYS установлен, ", "$" ver1 DB "версия: ", "$" ver2 DB ", номер модификации: ", "$" errmsg DB 13,10,"Ошибка с кодом ", "$" okmsg DB 13,10,"Успех!!!", "$" hmareq DB 13,10,"Запрашиваем область HMA", "$" hmarel DB 13,10,"Освобождаем область HMA", "$" enA20 DB 13,10,"Открываем линию A20", "$" dsA20 DB 13,10,"Закрываем линию A20", "$" loc_enA20 DB 13,10,"Локальный доступ к линии A20","$" loc_dsA20 DB 13,10,"Закрываем локальный доступ" DB " к линии A20", "$" check_A20 DB 13,10,"Проверяем доступность " DB "линии A20", "$" free_ext_mem DB 13,10,"Всего расширенной " DB "памяти, Кбайт: ", "$" max_ext_block DB 13,10,"Максимальный участок " DB "свободной" DB " расширенной памяти, Кбайт: ", "$"

HMMEntry dd ?

.CODE .STARTUP

mov ah, 9h ; Выводим заголовок mov dx, OFFSET msg int 21h

; Проверяем, установлен ли драйвер HIMEM.SYS

mov ax, 4300h int 2fh cmp al, 80h je HMM_installed

; Если не установлен, выводим сообщение и завершаем ; работу программы

mov ah, 9h mov dx, OFFSET noHMM int 21h

jmp terminate

HMM_installed:

mov ah, 9h mov dx, OFFSET yesHMM int 21h

; Получаем адрес управляющей функции драйвера

mov ax, 4310h int 2fh mov word ptr cs:[HMMEntry][0], bx mov word ptr cs:[HMMEntry][2], es

; Получаем номер версии

mov ah, 9h mov dx, OFFSET ver1 int 21h


mov ax,0 call cs:[HMMEntry]
; Выводим номер версии на экран
call Print_word
mov ah, 9h mov dx, OFFSET ver2 int 21h
mov ax, bx call Print_word
; Запрашиваем область HMA
mov ah, 9h mov dx, OFFSET hmareq int 21h
mov ax,0100h mov dx,0ffffh
call cs:[HMMEntry] or ax, ax jnz hmareq_ok jmp error
hmareq_ok: mov ah, 9h mov dx, OFFSET okmsg int 21h
; Открываем линию A20
mov ah, 9h mov dx, OFFSET enA20 int 21h
mov ax,0300h
call cs:[HMMEntry] or ax, ax jnz enA20_ok jmp error
enA20_ok: mov ah, 9h mov dx, OFFSET okmsg int 21h
; Закрываем линию A20
mov ah, 9h mov dx, OFFSET dsA20 int 21h
mov ax,0400h
call cs:[HMMEntry] or ax, ax jnz dsA20_ok jmp error
dsA20_ok:
mov ah, 9h mov dx, OFFSET okmsg int 21h
; Освобождаем область HMA
mov ah, 9h mov dx, OFFSET hmarel int 21h
mov ax,0200h
call cs:[HMMEntry] or ax, ax jz error
mov ah, 9h mov dx, OFFSET okmsg int 21h
; Получаем локальный доступ к линии A20
mov ah, 9h mov dx, OFFSET loc_enA20 int 21h
mov ax,0500h
call cs:[HMMEntry] or ax, ax jz error
mov ah, 9h mov dx, OFFSET okmsg int 21h
; Проверяем линию A20
mov ah, 9h mov dx, OFFSET check_A20 int 21h
mov ax,0700h
call cs:[HMMEntry] or ax, ax jz error
mov ah, 9h mov dx, OFFSET okmsg int 21h
; Определяем размер свободной расширенной памяти
mov ah, 9h mov dx, OFFSET free_ext_mem int 21h
mov ax,0800h
call cs:[HMMEntry]
push ax mov ax, dx call Print_word
mov ah, 9h mov dx, OFFSET max_ext_block int 21h
pop ax call Print_word
; Освобождаем линию A20
mov ah, 9h mov dx, OFFSET loc_dsA20 int 21h
mov ax,0600h
call cs:[HMMEntry] or ax, ax jz error
mov ah, 9h mov dx, OFFSET okmsg int 21h
jmp terminate
error: push bx
mov ah, 9h mov dx, OFFSET errmsg int 21h
pop ax call Print_word
terminate:
.EXIT 0
; Вывод на экран содержимого регистра AX
Print_word proc near ;-------------------- push ax push bx push dx ; push ax mov cl,8 rol ax,cl call Byte_to_hex mov bx,dx @@out_ch bh @@out_ch bl ; pop ax call Byte_to_hex mov bx,dx @@out_ch bh @@out_ch bl ; pop dx pop bx pop ax ret Print_word endp ; Byte_to_hex proc near ;-------------------- ; al - input byte ; dx - output hex ;-------------------- push ds push cx push bx ; lea bx,tabl mov dx,cs mov ds,dx ; push ax and al,0fh xlat mov dl,al ; pop ax mov cl,4 shr al,cl xlat mov dh,al ; pop bx pop cx pop ds ret ; tabl db '0123456789ABCDEF' Byte_to_hex endp ;


END
Теперь приведем пример программы, использующей область HMA для выполнения процедуры генерации звукового сигнала. Программа получает доступ к области HMA, копирует в нее процедуру генерации звукового сигнала и вызывает эту процедуру с помощью межсегментной команды call:
include sysp.inc
.MODEL tiny DOSSEG .STACK 100h
.DATA
msg DB 13,10,"Работа в области HMA", 13, 10 DB "Copyright ©Frolov A.,1991",13,10,13,10 DB "$"
noHMM DB 13,10,"Драйвер HIMEM.SYS " DB "не установлен", 13, 10, "$" yesHMM DB 13,10,"Драйвер HIMEM.SYS установлен, ","$" errmsg DB 13,10,"Ошибка с кодом ", "$" okmsg DB 13,10,"Успех!!!", "$" hmareq DB 13,10,"Запрашиваем область HMA", "$" hmarel DB 13,10,"Освобождаем область HMA", "$" enA20 DB 13,10,"Открываем линию A20", "$" dsA20 DB 13,10,"Закрываем линию A20", "$"
HMMEntry dd ?
HMAStart dd ?
.CODE .STARTUP
mov ah, 9h ; Выводим заголовок mov dx, OFFSET msg int 21h
; Проверяем, установлен ли драйвер HIMEM.SYS
mov ax, 4300h int 2fh cmp al, 80h je HMM_installed
; Если не установлен, выводим сообщение и завершаем ; работу программы
mov ah, 9h mov dx, OFFSET noHMM int 21h jmp terminate
HMM_installed:
mov ah, 9h mov dx, OFFSET yesHMM int 21h
; Получаем адрес управляющей функции драйвера
mov ax, 4310h int 2fh mov word ptr cs:[HMMEntry][0], bx mov word ptr cs:[HMMEntry][2], es
; Запрашиваем область HMA
mov ah, 9h mov dx, OFFSET hmareq int 21h
mov ax,0100h mov dx,0ffffh call cs:[HMMEntry] or ax, ax jnz hmareq_ok jmp error
hmareq_ok: mov ah, 9h mov dx, OFFSET okmsg int 21h
; Открываем линию A20
mov ah, 9h mov dx, OFFSET enA20 int 21h
mov ax,0300h call cs:[HMMEntry] or ax, ax jnz enA20_ok jmp error
enA20_ok: mov ah, 9h mov dx, OFFSET okmsg int 21h
; Записываем в двойное слово HMAStart ; адрес начала облсти HMA
mov word ptr cs:[HMAStart][0], 0010h mov word ptr cs:[HMAStart][2], 0ffffh


; Копируем в область HMA процедуру, которая ; будет там выполняться
cld lea si, begin_HMA_code mov di, 0010h
mov ax, cs mov ds, ax
mov ax, 0ffffh mov es, ax
mov cx, HMA_code_size rep movsb
; Вызываем процедуру, находящуюся в области HMA
call cs:[HMAStart]
; Закрываем линию A20
mov ah, 9h mov dx, OFFSET dsA20 int 21h
mov ax,0400h call cs:[HMMEntry] or ax, ax jnz dsA20_ok jmp error
dsA20_ok:
mov ah, 9h mov dx, OFFSET okmsg int 21h
; Освобождаем область HMA
mov ah, 9h mov dx, OFFSET hmarel int 21h
mov ax,0200h call cs:[HMMEntry] or ax, ax jz error
mov ah, 9h mov dx, OFFSET okmsg int 21h
jmp terminate
error:
push bx mov ah, 9h mov dx, OFFSET errmsg int 21h
pop ax call Print_word
terminate:
.EXIT 0
; Вывод на экран содержимого регистра AX
Print_word proc near ;-------------------- push ax push bx push dx ; push ax mov cl,8 rol ax,cl call Byte_to_hex mov bx,dx @@out_ch bh @@out_ch bl ; pop ax call Byte_to_hex mov bx,dx @@out_ch bh @@out_ch bl ; pop dx pop bx pop ax ret Print_word endp ; Byte_to_hex proc near ;-------------------- ; al - input byte ; dx - output hex ;-------------------- push ds push cx push bx ; lea bx,tabl mov dx,cs mov ds,dx ; push ax and al,0fh xlat mov dl,al ; pop ax mov cl,4 shr al,cl xlat mov dh,al ; pop bx pop cx pop ds ret ; tabl db '0123456789ABCDEF' Byte_to_hex endp
; Эта процедура предназначена для ; выполнения в области HMA. ; Она просто три раза генерирует ; звуковой сигнал.
begin_HMA_code:
BEEP BEEP BEEP retf
end_HMA_code:
; Здесь записана длина процедуры, ; предназначенной для выполнения в ; области HMA
HMA_code_size dw $-begin_HMA_code
END
В программе использована макрокоманда BEEP, описанная в файле sysp.inc:
; Макро для выдачи звукового сигнала
BEEP MACRO mov bx,0 mov ax, 0E07h int 10h ENDM
Следующая программа демонстрирует использование функции копирования. Сообщение копируется из области основной памяти в область расширенной памяти, а затем обратно в область основной памяти, но в другое место:
include sysp.inc


ExtMemMoveStruct struc
@Length dd ? ; количество пересылаемых байтов SourceHandle dw ? ; индекс исходного блока SourceOffset dd ? ; смещение в исходном блоке DestHandle dw ? ; индекс блока-назначения DestOffset dd ? ; смещение в блокe
ExtMemMoveStruct ends
.MODEL tiny
DOSSEG .STACK 100h
.DATA
movestr ExtMemMoveStruct <0,0,0,0,0>
msg DB 13,10,"Использование блоков EMB", 13, 10 DB "Copyright ©Frolov A.,1991",13,10,13,10 DB "$"
noHMM DB 13,10,"Драйвер HIMEM.SYS не установлен",13,10,"$" yesHMM DB 13,10,"Драйвер HIMEM.SYS установлен, ","$" errmsg DB 13,10,"Ошибка с кодом ", "$" okmsg DB 13,10,"Успех!!!", "$" free_ext_mem DB 13,10,"Всего расширенной памяти, Кбайт: ","$" max_ext_block DB 13,10,"Максимальный участок свободной" DB " расширенной памяти, Кбайт: ", "$" getEMBmsg DB 13,10,"Получаем блок EMB","$" freeEMBmsg DB 13,10,"Освобождаем блок EMB","$"
copymsg DB 13,10,"Копируем блок данных в область EMB","$" copymsg1 DB 13,10,"Копируем блок данных обратно","$"
testmsg DB 13,10,13,10,"Сообщение для копирования" DB " в область EMB","$"
len_testmsg DW $-testmsg
; Буфер для копирования сообщения
testbuf DB 512 dup(?)
HMMEntry dd ?
EMBHandle dw ?
.CODE .STARTUP
mov ah, 9h ; Выводим заголовок mov dx, OFFSET msg int 21h
; Проверяем, установлен ли драйвер HIMEM.SYS
mov ax, 4300h int 2fh cmp al, 80h je HMM_installed
; Если не установлен, выводим сообщение и завершаем ; работу программы
mov ah, 9h mov dx, OFFSET noHMM int 21h
jmp terminate
HMM_installed:
mov ah, 9h mov dx, OFFSET yesHMM int 21h
; Получаем адрес управляющей функции драйвера
mov ax, 4310h int 2fh mov word ptr cs:[HMMEntry][0], bx mov word ptr cs:[HMMEntry][2], es
; Определяем размер свободной расширенной памяти
mov ah, 9h mov dx, OFFSET free_ext_mem int 21h


mov ax,0800h
call cs:[HMMEntry]
push ax mov ax, dx call Print_word
mov ah, 9h mov dx, OFFSET max_ext_block int 21h
pop ax call Print_word
; Получаем блок EMB
mov ah, 9h mov dx, OFFSET getEMBmsg int 21h
mov ax,0900h mov dx,1h
call cs:[HMMEntry] or ax, ax jnz getemb_ok jmp error
getemb_ok:
mov EMBHandle, dx
mov ah, 9h mov dx, OFFSET okmsg int 21h
; Копируем строку testmsg в блок EMB
mov ah, 9h mov dx, OFFSET copymsg int 21h
; Заполняем управляющую структуру
; Длина копируемого массива памяти
mov ax, word ptr len_testmsg mov word ptr movestr.@Length, ax
; Индекс основной памяти, должен быть = 0
mov ax, 0 mov word ptr movestr.SourceHandle, ax
; Задаем сегмент:смещение копируемого сообщения
mov ax, OFFSET testmsg mov word ptr [movestr.SourceOffset][0], ax mov ax, cs mov word ptr [movestr.SourceOffset][2], ax
; Задаем индекс EMB, в который будем копировать ; сообщение из основной памяти
mov ax, EMBHandle mov movestr.DestHandle, ax
; Копируем в начало EMB, поэтому ; смещение = 0
mov ax, 0 mov word ptr [movestr.DestOffset][0], ax mov word ptr [movestr.DestOffset][2], ax
; Загружаем адрес управляющей структуры в DS:SI
mov ax, cs mov ds, ax mov ax, OFFSET movestr mov si, ax mov ax,0B00h
; Вызываем функцию копирования
call cs:[HMMEntry] or ax, ax jnz moveemb_ok jmp error
moveemb_ok:
mov ah, 9h mov dx, OFFSET okmsg int 21h
; Копируем сообщение обратно из блока EMB ; в буфер testbuf, расположенный в основной ; памяти
mov ah, 9h mov dx, OFFSET copymsg1 int 21h
; Подготавливаем управляющую структуру
mov ax, word ptr len_testmsg mov word ptr movestr.@Length, ax
mov ax, 0 mov word ptr movestr.DestHandle, ax
mov ax, OFFSET testbuf mov word ptr [movestr.DestOffset][0], ax mov ax, cs mov word ptr [movestr.DestOffset][2], ax
mov ax, EMBHandle mov movestr.SourceHandle, ax
mov ax, 0 mov word ptr [movestr.SourceOffset][0], ax mov word ptr [movestr.SourceOffset][2], ax
; Выполняем копирование
mov ax, cs mov ds, ax mov ax, OFFSET movestr mov si, ax mov ax,0B00h


call cs:[HMMEntry] or ax, ax jnz move1emb_ok jmp error
move1emb_ok:
mov ah, 9h mov dx, OFFSET okmsg int 21h
; Выводим скопированное сообщение на экран ; для проверки
mov ah, 9h mov dx, OFFSET testbuf int 21h
; Освобождаем блок EMB
mov ah, 9h mov dx, OFFSET freeEMBmsg int 21h
mov ax,0A00h mov dx,EMBHandle
call cs:[HMMEntry] or ax, ax jnz freeemb_ok jmp error
freeemb_ok:
mov EMBHandle, dx
mov ah, 9h mov dx, OFFSET okmsg int 21h
jmp terminate
error: push bx
mov ah, 9h mov dx, OFFSET errmsg int 21h
pop ax call Print_word
terminate:
.EXIT 0
; Вывод на экран содержимого регистра AX
Print_word proc near ;-------------------- push ax push bx push dx ; push ax mov cl,8 rol ax,cl call Byte_to_hex mov bx,dx @@out_ch bh @@out_ch bl ; pop ax call Byte_to_hex mov bx,dx @@out_ch bh @@out_ch bl ; pop dx pop bx pop ax ret Print_word endp ; Byte_to_hex proc near ;-------------------- ; al - input byte ; dx - output hex ;-------------------- push ds push cx push bx ; lea bx,tabl mov dx,cs mov ds,dx ; push ax and al,0fh xlat mov dl,al ; pop ax mov cl,4 shr al,cl xlat mov dh,al ; pop bx pop cx pop ds ret ; tabl db '0123456789ABCDEF' Byte_to_hex endp
END

Проверка подключения драйвера


Первое, что должна сделать программа, которая собирается вызывать драйвер HIMEM.SYS - проверить, был ли установлен этот драйвер при загрузке операционной системы.

Для этого надо загрузить в регистр AX значение 4300h и вызвать прерывание INT2Fh. Если после этого регистр AL будет содержать значение 80h, драйвер установлен, в противном случае - нет.

Приведем фрагмент программы, проверяющей подключение драйвера:

; Проверяем, установлен ли драйвер HIMEM.SYS

mov ax, 4300h int 2fh cmp al, 80h je HMM_installed ; Выполняем переход, ; если драйвер установлен



РАСШИРЕННАЯ ПАМЯТЬ


10.1.

10.2.

10.3.

10.4.

10.5.

10.6.

Компьютеры IBM AT, PS/2 всегда оснащены расширенной памятью, располагающейся в диапазоне адресов свыше одного мегабайта. Однако операционная система MS-DOS, использующая процессоры 80286, 80386 и 80486 в реальном режиме, не имеет полноценного доступа к этой памяти. То же относится и программам, разработанным для выполнения в среде MS-DOS. Единственное, что MS-DOS версий более ранних, чем 4.0, могла сделать с расширенной памятью - это разместить там быстродействующий электронный диск или кэш накопителя на магнитном диске.

Однако в составе MS-DOS версии 4.0 и более поздних версий появился драйвер расширенной памяти HIMEM.SYS, который в некоторой степени облегчает жизнь программистам, составляющим программы для MS-DOS. Этот драйвер расширяет основное адресное пространство 640K еще примерно на 64 килобайта и предоставляет относительно удобное средство для хранения в расширенной памяти массивов данных.

Будучи установлен в операционной системе, драйвер HIMEM.SYS предоставляет программам интерфейс в соответствии со спецификацией XMS (eXtended Memory Specification), разработанный корпорациями LOTUS, INTEL, MICROSOFT, AST Research.



Разблокирование EMB


На входе: AH = 0Dh;

DX = 16-битовый индекс (handle) разблокируемого EMB.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, A2h, AAh.

Функция разблокирует EMB, заблокированный при вызове предыдущей функции. Полученный от нее линейный адрес становится недействительным.



Спецификация XMS


Спецификация XMS содержит описание программного интерфейса драйвера HIMEM.SYS и рекомендации по использованию области памяти HMA.



Установка драйвера HIMEM.SYS


Для установки драйвера файл CONFIG.SYS должен содержать строку:

device=[d:][путь]himem.sys [/HMAMIN=h] [/NUMHANDLES=n]

Параметр /HMAMIN= (необязательный) задает минимальный размер памяти, который могут использовать программы в области HMA. Размер задается в килобайтах. Смысл использования этого параметра заключается в том, чтобы позволять использовать область HMA только тем программам, которые затребуют из этой области не меньше h килобайт. Это нужно для того, чтобы более эффективно использовать область HMA.

Если параметр не задан, используется по умолчанию значение 0. Это означает, что первая же программа, запросившая область HMA, получит к ней доступ. Программа, запущенная следом и, возможно, использующая эту память эффективнее, уже не сможет воспользоваться областью HMA.

Максимальное значение параметра h - 63.

Параметр /NUMHANDLES= задает максимальное количество областей расширенной памяти (блоков EMB), которое может быть запрошено программами. Диапазон задаваемых значений от 1 до 128, значение по умолчанию - 32. Задавая большие значения n, помните, что на управление каждым блоком EMB тратится 6 байт резидентной памяти.

При установке драйвер HIMEM.SYS может выдавать сообщения об ошибках в следующих случаях:

используется MS-DOS более старой версии, чем 3.00;

машина использует процессор 8086, а не 80286, 80386, или 80486;

при использовании компьютеров с нестандартными схемами управления расширенной памятью и линией A20.



Запросить область HMA


На входе: AH = 01h;

DX = размер памяти в байтах в области HMA, которая будет использоваться резидентными программами или драйверами, обычная программа должна использовать значение DX=FFFFh.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка.

Ошибки: BL = 80h, 81h, 90h, 91h, 92h (описание кодов ошибок будет приведено после описания всех функций).

С помощью этой функции программа может зарезервировать для себя область HMA. Задаваемый в регистре DX размер памяти сравнивается с указанным в параметре драйвера /HMAMIN=. Область HMA распределяется запросившей программе только в том случае, если запрошенный в регистре DX размер больше или равен указанному в параметре /HMAMIN. Такой механизм позволяет ограничить использование области HMA только теми программами, которые используют ее наилучшим образом.

Поясним это на примере. Пусть при инициализации операционной системы из файла AUTOEXEC.BAT запускаются две программы. Одна из них использует 10 килобайт из области HMA и запускается первой (в регистре DX функции 01h эта программа указывает значение 10240). Вторая запускаемая программа использует 40 килобайтов и запускается после первой. Очевидно, что вторая программа использует область HMA более эффективно. Но так как область HMA уже распределена первой программе, вторая программа не сможет ее использовать.

Задавая параметр /HMAMIN=40, мы запретим распределение области HMA тем программам, которые используют в ней меньше 40 килобайтов. Теперь первая программа не получит доступ к области HMA, даже если она будет запускаться до второй, использующей 40 килобайтов памяти из области HMA.



Запросить область UMB


На входе: AH = 10h;

DX = размер запрашиваемого блока UMB в параграфах.

На выходе: AX = 0001h - если функция выполнена успешно 0000h - если произошла ошибка;

BX = сегмент полученного UMB;

DX = размер полученного блока или размер максимального свободного блока UMB (если невозможно выделить блок требуемого размера).

Ошибки: BL = 80h, B0h, B1h.

Эта функция позволяет программе получить дступ к блокам UMB, лежащих в пределах первого мегабайта адресного пространства. Для использования этих блоков не требуется управлять линией A20.

Если вам надо определить размер доступной области UMB, задайте при вызове этой функции DX=FFFFh.



ДОПОЛНИТЕЛЬНАЯ ПАМЯТЬ


11.1.

11.2.

11.3.

11.4.

11.5.

11.6.

11.7.

В отличие от расширенной памяти дополнительная память с помощью специальной аппаратуры и программного обеспечения отображается в диапазон адресов, лежащий ниже границы 1 Мбайт. Такой способ пригоден для компьютеров, использующих процессор Intel 8086, не обладающий возможностью адресации расширенной памяти.

Чтобы понять, как происходит отображение, вспомним распределение первого мегабайта оперативной памяти. Область с адресами от 00000 до 9FFFF - это стандартная память размером 640К. (Мы используем здесь физический 20-разрядный адрес). Диапазон адресов от A0000 до BFFFF используется видеоадаптерами. Наконец, 256 Кбайтов с адресами C0000 - FFFFF используется для BIOS.

Однако обычно BIOS не занимает все 256 Кбайтов адресного пространства, оставляя "окна" размером в десятки килобайтов. С помощью специальной аппаратуры можно отображать эти окна на участки дополнительной памяти, как бы "подставляя" участки памяти под адреса "окон".

В практике построения вычислительных систем на основе микропроцессоров такая техника используется уже давно. Для компьютеров IBM PC/XT/AT корпорации Lotus Development, Intel и Microsoft разработали спецификацию расширенной памяти (Expanded Memory Specification - EMS). В настоящий момент распространены версии 3.2 и 4.0 этой спецификации.

Существует альтернативная спецификация Extended Expanded Memory Specification - EEMS), отличающаяся от версии EMS 3.2 в основном наличием поддержки мультизадачных операционных систем. Эта спецификация была разработана другой группой крупных производителей компьютерного оборудования: AST Research, Ashton-Tate, Quadram. Однако эта спецификация не получила широкого распространения. Версия 4.0 EMS включила в себя все расширения спецификации EEMS.

В спецификации EMS в качестве окна для доступа к дополнительной памяти используются 64 килобайта, расположенные по адресам C0000h - EFFFFh. Это окно в спецификации называется "page frame". Окно разбивается на четыре сегмента по 16 килобайтов. Вся дополнительная память разбивается на логические страницы (logical page) размером по 16 килобайтов. Любая логическая страница может быть отображена на любой сегмент окна доступа. Таким образом, используя четыре сегмента, программа может адресоваться одновременно к любым четырем логическим страницам дополнительной памяти.

На рисунке схематически показано отображение логических страниц дополнительной памяти на сегменты 64-килобайтного окна, расположенного в области адресов ПЗУ:



Дополнительные функции EMM


Дополнительные функции используются резидентными программами, драйверами и мультизадачными приложениями. Кроме того, с помощью этих функций можно выполнять пересылку массивов в дополнительной и стандартной памяти, а также организовать подкачку и выполнение программных модулей в дополнительной памяти.

Обратите внимание, что использование расширенной памяти в спецификации XMS позволяет выполнять программные модули (с ограничениями) только в области HMA размером примерно 64 килобайта. Остальная расширенная память может быть использована только для хранения данных.

Специальные функции с номерами 47h - 4Eh поддерживаются EMM версии 3.2, функции с номерами 4Fh - 58h - только EMM версии 4.0.



Драйверы дополнительной памяти


Для использования дополнительной памяти в компьютер должна быть вставлена плата дополнительной памяти и в файле CONFIG.SYS подключен специальный драйвер, который обычно поставляется вместе с платой памяти. Драйвер выполняет управление дополнительной памятью и называется EMM (Expanded Memory Manager).

Операционная система MS-DOS версии 4.01 содержит драйвер XMA2EMS.SYS, реализующий функции управления дополнительной памятью. Этот драйвер должен быть подключен в файле CONFIG.SYS следующим образом:

DEVICE=XMA2EMS.SYS [FRAME=xxxx] [Pnnn=xxxx] [/X:pages]

Параметр FRAME задает базовый адрес для 64-килобайтового окна доступа в виде шестнадцатеричного сегментного адреса, например C000. Этот адрес должен находиться в диапазоне C000 - E000.

Параметр Pnnn позволяет задать базовый адрес для конкретной страницы дополнительной памяти. Здесь nnn - это номер страницы (0-255), xxxx - сегментный адрес в шестнадцатеричном формате. При использовании параметра FRAME нельзя указывать параметры P0, P1, P2, P3.

Параметр /X:pages определяет, сколько страниц дополнительной памяти будет использовано. По умолчанию используется вся дополнительная память.

Если ваш компьютер имеет процессор 80386 и расширенную память, вы можете использовать драйвер EMM386.SYS, поставляемый в составе MS-DOS версии 4.01. Этот драйвер эмулирует дополнительную память на расширенной памяти. При этом несколько снижается производительность системы.

Драйвер может быть подключен следующим образом:

DEVICE=EMM386.SYS [size] [X:mmmm-nnnn] [Mx]

Параметр size определяет количество используемой драйвером расширенной памяти в килобайтах. Значение по умолчанию - 256 Кбайт.

Параметр X:mmmm-nnnn определяет диапазон памяти, которая не должна быть использована для размещения окон доступа.

Параметр Mx задает расположение окна доступа, используемого для отображения логических страниц дополнительной памяти. Соответствие параметра x сегментному адресу окна приведено в таблице:

x Адрес окна доступа

0 C000 1 C400 2 C800 3 CC00 4 D000 5 D400 6 D800 7 DC00 8 E000



Изменение размера пула


На входе: AX = 5100h;

BX = новый размер пула в логических страницах;

DX = индекс EMM.

На выходе: AH = байт состояния EMM.

С помощью этой функции программа может изменить размер уже заказанного ранее пула страниц дополнительной памяти.



Коды ошибок


Все функции EMM возвращают код ошибки в регистре AH:

Код Ошибка
00h Нет ошибки, нормальное завершение
80h Внутренняя ошибка драйвера EMM
81h Ошибка аппаратуры EMS-памяти
82h EMM занят
83h Неправильный индекс пула
84h Неправильный номер запрошенной функции
85h Больше нет доступных индексов пулов
86h Ошибка при выполнении сохранения или восстановления контекста отображения
87h Запрошено больше памяти, чем общее количество доступной EMS-памяти
88h Запрошено больше страниц, чем доступно
89h Нельзя открыть индекс пустого пула
8Ah Пул не содержит так много страниц
8Bh Неправильное отображение, заданы номера
физических страниц, отличные от 0 - 3
8Ch Переполнена область сохранения контекста отображения
8Dh Многократное сохранение контекста для одного пула
8Eh Попытка восстановления несохраненного контекста
8Fh Неправильный номер подфункции в регистре AL
90h Неправильный тип атрибута
91h Не поддерживается неразрушаемая память
92h Произошло перекрытие исходной и результирующей областей (это не ошибка, а предупреждение)
93h Область назначения, заданная индексом, слишком мала
94h Стандартная память перекрывается дополнительной памятью
95h Слишком большое смещение при пересылке блока
96h Слишком большой размер блока, больше 1 мегабайта
97h Заданы одинаковые исходный и результирующий индексы
98h Задан неправильный тип памяти (смещение 4)
A0h Заданному имени не соответствует ни один пул
A1h Заданное имя уже существует
A2h Длина исходной области больше 1 мегабайта
A3h Содержимое заданного блока данных неверно
A4h Доступ к этой функции запрещен



Найти имя пула


На входе: AH = 54h;

AL = код подфункции:

0 - получить каталог пулов; 1 - найти пул по имени; 2 - определить количество открытых пулов;

DS:SI = адрес буфера имени пула для подфункции 1, длина этого буфера должна быть 8 байтов;

ES:DI = адрес массива 10-байтовых элементов; в первое слово элемента будет записан индекс пула, в остальные 8 - имя пула.

На выходе: AH = байт состояния EMM;

AL = количество элементов в массиве (подфункция 0);

DX = индекс найденного пула (для подфункции 1);

BX = количество открытых пулов (для подфункции 2).

Эта функция позволяет определить индекс пула по его имени или получить каталог всех именованных пулов.



Определить количество активных пулов


На входе: AX = 4C00h;

DX = индекс EMM.

На выходе: AH = байт состояния EMM;

BX = количество активных пулов дополнительной памяти.

Функция предназначена для определения количества текущих активных пулов и может вызываться перед использованием следующей функции (с номером 4Dh).



Определить количество страниц в пуле


На входе: AX = 4B00h;

DX = индекс EMM.

На выходе: AH = байт состояния EMM;

BX = количество логических страниц в пуле.

Функция возвращает количество логических страниц дополнительной памяти для выбранного пула.



Открыть индекс EMM


На входе: AX = 4300h;

BX = требуемое в данном пуле количество логических страниц.

На выходе: AH = байт состояния EMM;

DX = индекс пула EMS, он будет использоваться в операциях с пулом логических страниц.

Эта функция позволяет заказать пул логических страниц (т.е. некоторую совокупность логических страниц дополнительной памяти). Полученному пулу присваивается индекс (handle), который указывает на пул и используется во всех операциях с пулом.

/** *.Name ems_open *.Title Открытие индекса пула страниц EMS * *.Descr Эта функция открывает индекс пула страниц * EMS, делая доступными логические страницы * дополнительной памяти. * *.Proto int ems_open(int n_pages, int *handle); * *.Params int n_pages - количество требуемых логических * страниц; * int *handle - указатель на слово, в которое * будет записан индекс полученного пула. * *.Return Байт состояния драйвера EMS * *.Sample ems_test.c **/

#include <stdio.h> #include <dos.h> #include "sysp.h"

int ems_open(int n_pages, int *handle) {

union REGS reg;

reg.x.ax = 0x4300; reg.x.bx = n_pages; int86(0x67, &reg, &reg);

*handle = reg.x.dx; return(reg.h.ah); }



Отображение/запрещение группы страниц


На входе: AH = 50h;

AL = код подфункции:

0 - разрешить или запретить отображение страниц, используя номера страниц;

1 - аналогично подфункции 0, но используются не номера страниц, а адреса сегментов;

DS:SI = адрес массива структур, состоящий из двух слов, первое слово - номер логической страницы, второе - номер физической страницы; указание логической страницы 0FFFFh запрещает отображение страницы;

CX = размер массива DS:SI в количестве структур.

На выходе: AH = байт состояния EMM.

Функция позволяет за один вызов разрешить и запретить использование нескольких страниц.



Отобразить память


На входе: AH = 44h;

AL = номер физической страницы окна доступа (от 0 до 3);

BX = номер логической страницы из числа находящихся в пуле страниц (от 0 до n-1, где n - количество логических страниц в пуле); для версии EMS 4.0 задание значения 0FFFFh приводит к запрещению отображения физических страниц пула, для разрешения их отображения необходимо вызвать эту функцию еще раз, указав правильный номер страницы;

DX = индекс EMM, полученный от функции 43h.

На выходе: AH = байт состояния EMM.

Функция выполняет отображение (привязку) одной из логических страниц пула к одному их четырех 16-килобайтных сегментов окна просмотра, т.е. к физическим страницам.

/** *.Name ems_map *.Title Отобразить память EMS * *.Descr Эта функция отображает логические страницы * пула дополнительной памяти на физические. * *.Proto int ems_map(int phys_page, int log_page, * int handle); * *.Params int phys_pages - номер физической страницы * окна доступа (от 0 до 3), на которую необходимо * отобразить логическую страницу пула; * * int_log_page - номер логической страницы пула; * * int *handle - индекс полученного пула; * *.Return Байт состояния драйвера EMS * *.Sample ems_test.c **/

#include <stdio.h> #include <dos.h> #include "sysp.h"

int ems_map(int phys_page, int log_page, int handle) {

union REGS reg;

reg.h.ah = 0x44; reg.h.al = phys_page; reg.x.bx = log_page; reg.x.dx = handle; int86(0x67, &reg, &reg);

return(reg.h.ah); }



Отобразить страницу и перейти по адресу


На входе: AH = 55h;

AL = код подфункции:

0 - использовать массив номеров физических страниц;

1 - использовать массив сегментных адресов;

DS:SI = адрес структуры MapAndJump длиной 9 байтов.

На выходе: AH = байт состояния EMM.

Эта функция предназначена для перекачки страниц исполняемого кода в память и последующего выполнения этого кода.

Первые четыре байта структуры MapAndJump содержат смещение и сегментный адрес, по которым должен быть выполнен переход. Следующий байт - количество элементов в таблице отображения. Последние 4 байта содержат FAR-адрес таблицы отображения, состоящей из 4-байтовых элементов. Первое слово элемента таблицы отображения - номер логической страницы, второе - номер физической страницы.



Отобразить страницу и вызвать процедуру


На входе: AH = 56h;

AL = код подфункции:

0 - использовать массив номеров физических страниц;

1 - использовать массив сегментных адресов;

2 - получить размер стека, необходимого для использования подфункций 0 и 1;

DS:SI = адрес структуры MapAndCall длиной 22 байта.

На выходе: AH = байт состояния EMM;

BX = требуемый размер стека (заполняется при выполнении подфункции 2).

Функция работает аналогично предыдущей, но не передает управление исполняемому коду, а вызывает его как процедуру.

Первые 9 байтов структуры MapAndCall соответствуют структуре MapAndJump. Далее идет еще один байт длины таблицы отображения и 4 байта адреса другой таблицы отображения. Вторая таблица описывает отображение страниц, которое будет установлено после вызова процедуры. Последние 8 байтов структуры зарезервированы для дальнейшего использования.



Переслать/обменять область памяти


На входе: AH = 57h;

AL = код подфункции:

0 - переслать область памяти; 1 - обменять область памяти;

DS:SI = адрес структуры MoveInfo длиной 18 байтов.

На выходе: AH = байт состояния EMM.

Функция предназначена для выполнения перемещения или обмена содержимого областей стандартной или дополнительной памяти. Возможно перекрытие исходной и результирующей областей памяти. Максимальный размер блоков, над которыми эта функция может выполнять операции - 1 мегабайт.

Структура MoveInfo содержит всю необходимую информацию о расположении блоков памяти:

Смещение Размер Описание

(+0) 4 Размер блока в байтах

(+4) 1 Тип исходной памяти: 0 - стандартная, 1 - EMS

(+5) 2 Индекс исходной памяти: 0 для стандартной памяти, индекс пула для EMS

(+7) 2 Смещение для исходной памяти (внутри сегмента или страницы)

(+9) 2 Адрес исходного сегмента или номер для исходной страницы

(+11) 1 Тип результирующей памяти: 0 - стандартная, 1 - EMS

(+12) 2 Индекс результирующей памяти: 0 для стандартной памяти, индекс пула для EMS

(+14) 2 Смещение для результирующей памяти (внутри сегмента или страницы)

(+16) 2 Адрес результирующего сегмента или номер для исходной страницы

Получить массив адресов отображения

На входе: AH = 58h;

AL = код подфункции:

0 - получить массив отображения; 1 - получить размер массива отображения;

ES:DI = адрес буфера для массива отображения.

На выходе: AH = байт состояния EMM;

CX = количество элементов в массиве отображения (для подфункции 1)

Массив отображения, получаемый при помощи этой функции, состоит из 4-байтовых элементов. Первое слово элемента содержит адрес сегмента, второе - номер физической страницы, соответствующей этому адресу.



Получить информацию о пулах


На входе: AX = 4D00h;

ES:DI = адрес буфера для информации.

На выходе: AH = байт состояния EMM;

BX = количество активных пулов дополнительной памяти.

После вызова функции буфер заполняется как массив структур, содержащих два 16-битовых слова. Первое слово содержит индекс пула, второе - количество логических страниц в этом пуле.



Получить номер версии EMM


На входsе: AX = 4600h.

На выходе: AH = байт состояния EMM;

AL = номер версии в двоично-десятичном (BCD) формате, 32h соответствует версии 3.2.

Версия 4.0 EMM поддерживает больше функций по управлению дополнительной памятью, чем версия 3.2. Прежде чем использовать такие функции, следует определить номер используемой версии EMM с помощью функции 46h.

/** *.Name ems_ver *.Title Определение версии драйвера EMS * *.Descr Эта функция возвращает номер версии * драйвера EMS в двоично-десятичном формате. * *.Proto int ems_ver(char *ver); * *.Params char *ver - указатель на байт, в который * будет записан номер версии. * *.Return Номер версии драйвера EMS в формате BCD * *.Sample ems_test.c **/

#include <stdio.h> #include <dos.h> #include "sysp.h"

int ems_ver(char *ver) {

union REGS reg;

reg.x.ax = 0x4600; int86(0x67, &reg, &reg);

*ver = reg.h.al; return(reg.h.ah); }



Получить размер доступной памяти EMS


На входе: AX = 4200h.

На выходе: AH = байт состояния EMM;

DX = общее количество 16-килобайтных страниц EMS в системе;

BX = число доступных в настоящее время страниц EMS.

Эта функция позволяет вам получить информацию о наличии и доступности страниц дополнительной памяти.

/** *.Name ems_page *.Title Определение количества страниц EMS * *.Descr Эта функция предназначена для определения * общего количества страниц EMS и количества * страниц, доступных в настоящее время. * *.Proto char ems_page(unsigned *total, unsigned *free); * *.Params unsigned *total - указатель на переменную, * в которую будет записано общее количество * страниц памяти EMS; * unsigned *free - указатель на переменную, * в которую будет записано количество * доступных страниц памяти EMS; * *.Return Сосотояние EMM. * *.Sample ems_test.c **/

#include <stdio.h> #include <dos.h> #include "sysp.h"

char ems_page(unsigned *total, unsigned *free) {

union REGS reg;

reg.x.ax = 0x4200; int86(0x67, &reg, &reg); *total = reg.x.dx; *free = reg.x.bx;

return(reg.h.ah); }



Получить сегмент окна


На входе: AX = 4100h.

На выходе: AH = байт состояния EMM;

BX = сегмент окна для доступа к логическим страницам дополнительной памяти.

Функция позволяет получить сегмент 64-килобайтного окна, используемого драйвером EMS для доступа к логическим страницам расширенной памяти.

/** *.Name ems_fram *.Title Определение сегмента окна доступа * *.Descr Эта функция возвращает сегментный адрес * окна, которое используется для доступа к * дополнительной памяти. * *.Proto char ems_fram(unsigned *frame); * *.Params unsigned *frame - Указатель на переменную, * в которую будет записан сегментный * адрес окна доступа. * *.Return Сосотояние EMM. * *.Sample ems_test.c **/

#include <stdio.h> #include <dos.h> #include "sysp.h"

char ems_fram(unsigned *frame) {

union REGS reg; struct SREGS sreg;

reg.x.ax = 0x4100; int86(0x67, &reg, &reg); *frame = reg.x.bx;

return(reg.h.ah); }



Получить состояние EMM


На входе: AX = 4000h.

На выходе: AH = байт состояния EMM.

Эта функция используется для проверки состояния драйвера EMM. Она должна использоваться только после того, как программа убедилась в наличии драйвера EMM.

Для получения состояния EMM используйте следующую функцию:

/** *.Name ems_stat *.Title Определение состояния драйвера EMS * *.Descr Эта функция возвращает байт состояния * драйвера EMS * *.Proto char ems_stat(void); * *.Params Не используются * *.Return Байт состояния драйвера EMS * *.Sample ems_test.c **/

#include <stdio.h> #include <dos.h> #include "sysp.h"

char ems_stat(void) {

union REGS reg; struct SREGS sreg;

reg.x.ax = 0x4000; int86(0x67, &reg, &reg); return(reg.h.ah); }



Получить/установить атрибуты пула


На входе: AH = 52h;

AL = код подфункции:

0 - получить атрибуты пула;

1 - установить атрибуты пула;

2 - определить возможность использования атрибута неразрушаемой памяти;

BL = новые атрибуты;

DX = индекс EMM.

На выходе: AH = байт состояния EMM;

AL = для подфункции 0: атрибуты пула;

для подфункции 2:

0 - атрибут неразрушаемой памяти недоступен;

1 - атрибут неразрушаемой памяти доступен.

Функция дает возможность установить для некоторых пулов атрибут неразрушаемой памяти. Содержимое таких пулов не исчезает при теплой перезагрузке операционной системы (после нажатии комбинации клавиш Ctrl-Alt-Del).



Получить/установить отображение части страниц


На входе: AH = 4Fh;

AL = код подфункции:

0 - получить содержимое регистров отображения в буфер ES:DI;

1 - восстановить содержимое регистров отображения из буфера ES:DI;

2 - определить размер требуемого буфера;

ES:DI = адрес буфера для информации;

DS:SI = адрес массива 16-битовых слов, содержащих сегментные адреса страниц, для которых необходимо сохранить контекст отображения; самое первое слово массива - это размер массива в словах.

На выходе: AH = байт состояния EMM;

AL = для подфункции 2 - размер буфера.

Функция работает аналогично предыдущей, но позволяет сохранять контексты только для некоторых страниц. Это экономит время, необходимое для сохранения/восстановления.



Получить/установить отображение всех страниц


На входе: AH = 4Eh;

AL = код подфункции:

0 - получить содержимое всех регистров отображения в буфер ES:DI;

1 - восстановить содержимое всех регистров отображения из буфера ES:DI;

2 - получить и установить все регистры отображения в буфер ES:DI или восстановить из буфера ES:DI (т.е. комбинация подфункций 0 и 1);

3 - Определить размер требуемого буфера;

ES:DI = адрес буфера для информации;

На выходе: AH = байт состояния EMM;

AL = для подфункции 3 - размер буфера.

Эта функция предназначена для поддержки мультизадачности. Она позволяет быстро изменять контекст дополнительной памяти при переключении с одного процесса на другой.



Программа, использующая EMS


Приведенная ниже программа демонстрирует использование основных функций EMM:

#include <stdio.h> #include <conio.h> #include "sysp.h"

void main(void); void main(void) {

unsigned frame, err; unsigned total, free; unsigned handle; char ver;

// Это сообщение будет переписано сначала из // области стандартной памяти в область дополнительной, // затем обратно в область основной.

static char test_msg[] = "Тестовое сообщение для " "записи в область EMS.";

char buf[80]; char _far *ptr; int i;

// Проверяем, установлен ли драйвер EMS.

if(ems_init() != 0) { printf("\nДрайвер EMS не установлен."); exit(-1); }

// Если драйвер установлен, определяем его состояние.

printf("\nДрайвер EMS установлен, состояние: %02.2X.", ems_stat());

// Выводим номер версии драйвера

if((err = ems_ver(&ver)) != 0) { printf("\nОшибка: %02.2X", err); exit(-1); } printf("\nВерсия EMM: %02.2X", ver);

// Определяем сегмент окна доступа

if((err = ems_fram(&frame)) != 0) { printf("\nОшибка: %02.2X", err); exit(-1); }

printf("\nСегмент окна доступа: %04.4X", frame);

// Определяем общее количество страниц и // количество доступных страниц.

if((err = ems_page(&total, &free)) != 0) { printf("\nОшибка: %02.2X", err); exit(-1); }

printf("\nОбщее количество страниц EMS: %d" "\nКоличество доступных страниц: %d", total, free);

// Заказываем пул в дополнительной памяти.

if((err = ems_open(free, &handle)) != 0) { printf("\nОшибка: %02.2X", err); exit(-1); }

// Отображаем нулевую физическую страницу // на нулевую логическуб страницу пула.

if((err = ems_map(0, 0, handle)) != 0) { printf("\nОшибка: %02.2X", err); exit(-1); }

// Конструируем указатель на физическую страницу.

ptr = FP_MAKE(frame,0);

// Копируем тестовое сообщение в // дополнительную память.

printf("\nКопируем в EMS: %s",test_msg);

for(i=0; test_msg[i] != 0; i++) ptr[i] = test_msg[i];

// Теперь копируем это сообщение обратно // в стандартную память.

for(i=0; ptr[i] != 0; i++) buf[i] = ptr[i]; buf[i] = 0;

// Выводим сообщение на экран для // проверки.

printf("\nСкопировано из EMS: %s",buf);

// Закрываем пул.

if((err = ems_clos(&handle)) != 0) { printf("\nОшибка: %02.2X", err); exit(-1); }

exit(0); }



Проверка подключения драйвера


Драйвер дополнительной памяти устанавливает вектор прерывания INT67h таким образом, что этот вектор указывает на заголовок драйвера. При изучении драйверов мы рассказывали вам о формате заголовка. Сейчас для нас важно, что со смещением 10 в заголовке располагается имя драйвера - "EMMXXXX0". Следовательно, для проверки подключения драйвера мы можем, получив адрес заголовка, сравнить восемь байтов имени устройства со строкой "EMMXXXX0". При совпадении мы можем считать, что драйвер дополнительной памяти установлен.

Для проверки установки драйвера вы можете использовать следующую функцию, выполняющую все описанные действия:

/** *.Name ems_init *.Title Функция проверяет установку драйвера EMS * *.Descr Эта функция проверяет наличие драйвера EMS * *.Proto int ems_init(void); * *.Params Не используются * *.Return 0 - драйвер EMS установлен; * 1 - драйвер EMS не установлен. * *.Sample ems_test.c **/

#include <stdio.h> #include <dos.h> #include "sysp.h"

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);

}



Сохранить контекст отображения


На входе: AX = 4700h;

DX = индекс EMM.

На выходе: AH = байт состояния EMM.

Эта функция предназначена для использования драйверами и резидентными программами. Прежде чем работать с дополнительной памятью, такие программы должны сохранить текущий контекст отображения логических страниц. Перед возвратом управления прикладной программе контекст должен быть восстановлен с помощью функции 48h.



Стандартные функции EMM


Стандартные функции - это небольшое подмножество функций EMM, необходимое для работы обычных прикладных программ (не резидентных и не драйверов). Все эти функции поддерживаются EMM версии 3.2.



Установить/прочитать имя пула


На входе: AH = 53h;

AL = код подфункции:

0 - получить имя пула; 1 - установить имя пула;

ES:DI = адрес буфера имени пула, длина этого буфера должна быть 8 байтов;

DX = индекс EMM.

На выходе: AH = байт состояния EMM.

Функция предназначена для использования в мультизадачной среде. Она позволяет нескольким процессам, работающим одновременно, использовать одни и те же именованные пулы дополнительной памяти.



Восстановить контекст отображения


На входе: AX = 4800h;

DX = индекс EMM.

На выходе: AH = байт состояния EMM.

Функция позволяет восстановить контекст отображения логических страниц пула, сохраненный предыдущей функцией.



Вызов функций драйвера


Для вызова функций драйвера дополнительной памяти программа должна загрузить код функции в регистр AH, код подфункции (обычно 0) в регистр AL, и затем вызвать прерывание INT 67h.

После возврата из прерывания в регистр AH будет записано слово состояния. При успешном выполнении функции оно равно 0.



Закрыть индекс EMM


На входе: AX = 4500h;

DX = индекс EMM.

На выходе: AH = байт состояния EMM.

Функция освобождает все логические страницы пула. После освобождения эти страницы могут быть повторно распределены.

/** *.Name ems_clos *.Title Закрытие индекса пула страниц EMS * *.Descr Эта функция закрывает индекс пула страниц, * полученный функцией ems_open(). * *.Proto int ems_clos(int *handle); * *.Params int *handle - указатель на слово, в которое * будет записан индекс полученного пула. * *.Return Байт состояния драйвера EMS * *.Sample ems_test.c **/

#include <stdio.h> #include <dos.h> #include "sysp.h"

int ems_clos(int *handle) {

union REGS reg;

reg.x.ax = 0x4500; reg.x.dx = *handle; int86(0x67, &reg, &reg);

return(reg.h.ah); }