Современные компьютеры всегда оснащены расширенной памятью, располагающейся в диапазоне адресов свыше одного мегабайта. Однако операционная система MS-DOS, которая была создана для реального режима работы процессоров, не имеет полноценного доступа к этой памяти. То же относится и программам, предназначенным для выполнения в среде MS-DOS. Единственное, что MS-DOS старых версий (более ранних, чем 4.0), могла сделать с расширенной памятью - это разместить там быстродействующий электронный диск или кэш накопителя на магнитном диске.
Однако в составе MS-DOS версии 4.0 и более поздних версий появился драйвер расширенной памяти HIMEM.SYS, который в некоторой степени облегчает жизнь программистам, составляющим программы для MS-DOS. Этот драйвер расширяет основное адресное пространство 640 Kбайт еще примерно на 64 Кбайт и предоставляет относительно удобное средство для хранения в расширенной памяти массивов данных.
Будучи установлен в операционной системе, драйвер HIMEM.SYS обеспечивает программный интерфейс в соответствии со спецификацией XMS (eXtended Memory Specification), разработанный корпорациями Lotus, Intel, Microsoft и AST Research.
При обсуждении спецификации XMS мы будем использовать следующие понятия и термины:
На рис. 11.1 схематично показано расположение различных перечисленных выше блоков памяти в адресном пространстве.
Рис. 11.1. Расположение различных блоков памяти в адресном пространстве
Для установки драйвера файл 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 XE "HIMEM.SYS" может выдавать сообщения об ошибках в следующих случаях:
Спецификация XMS содержит описание программного интерфейса драйвера HIMEM.SYS и рекомендации по использованию области памяти HMA.
Первое, что должна сделать программа, которая собирается вызывать драйвер HIMEM.SYS - проверить, был ли установлен этот драйвер при загрузке операционной системы.
Для этого надо загрузить в регистр AX значение 4300h и вызвать прерывание INT 2Fh. Если после этого регистр AL будет содержать значение 80h, драйвер установлен, в противном случае - нет.
Приведем фрагмент программы, проверяющей подключение драйвера:
; Проверяем, установлен ли драйвер HIMEM.SYS mov ax, 4300h int 2fh cmp al, 80h ; Если драйвер установлен, выполняем переход je HMM_installed
Для вызова драйвера программа должна получить адрес специальной управляющей программы, которая выполняет все функции по обслуживанию расширенной памяти и области 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 байт.
Все функции драйвера HIMEM.SYS могут быть разделены на следующие пять групп:
Приведем подробное описание этих функций в соответствии со спецификацией XMS версии 2.0.
Регистры на входе: |
AH = 00h |
Регистры на выходе: |
BX = номер внутренней модификации драйвера; |
Функция возвращает номера версии и модификации XMS в двоично-десятичном (BCD) формате. Например, если AX=0250h, это означает, что драйвер соответствует спецификации XMS версии 2.50. Дополнительно функция позволяет проверить наличие в системе области HMA.
Регистры на входе: |
AH = 01h |
Регистры на выходе: |
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.
Регистры на входе: |
AH = 02h |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, 81h, 90h, 93h |
Программы, которые запрашивали область HMA, должны освобождать ее после использования при помощи этой функции. При этом данные, которые находились в этой области, будут потеряны.
После того, как программа освободила область HMA, эта область становится доступной другим программам.
Регистры на входе: |
AH = 03h |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, 81h, 82h |
Эта функция предназначена для тех программ, которые будут использовать область HMA. Она разрешает работу заблокированной по умолчанию 21 адресной линии процессора. Перед возвратом управления системе программа должна закрыть линию A20 с помощью функции 04h.
Следует отметить, что для многих типов системных плат переключение линии A20 - достаточно медленная операция.
Регистры на входе: |
AH = 04h |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, 81h, 82h, 94h |
Функция предназначена для тех программ, которые используют область HMA. Она должна выполняться перед завершением работы такой программы.
Регистры на входе: |
AH = 05h |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, 81h, 82h |
Эта функция предназначена только для тех программ, которые непосредственно управляют расширенной памятью. Перед завершением работы программа должна закрыть линию A20 при помощи функции 06h.
Регистры на входе: |
AH = 06h |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, 81h, 82h, 94h |
Функция отменяет разрешение линии A20, запрошенное предыдущей функцией. Она предназначена только для тех программ, которые непосредственно управляют расширенной памятью.
Регистры на входе: |
AH = 07h |
Регистры на выходе: |
AX = 0001h - если линия A20 открыта, 0000h - если линия A20 закрыта |
Ошибки: |
BL = 00h, 80h, 81h |
Функция выполняет попытку адресоваться за границу 1 Мбайт памяти и проверяет, не происходит ли при этом обращение в начало памяти (то есть "свертка памяти").
Регистры на входе: |
AH = 08h |
Регистры на выходе: |
AX = размер наибольшего свободного блока расширенной памяти, Кбайт; |
Ошибки: |
BL = 80h, 81h, A0h |
При определении размера свободной расширенной памяти в возвращаемое значение не включается 64 Кбайт области HMA, даже если эта область не используется программами.
Регистры на входе: |
AH = 09h |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка; |
Ошибки: |
BL = 80h, 81h, A0h, A1h |
Функция заказывает блок EMB из пула свободной расширенной памяти. При успешном выполнении запроса функция возвращает идентификатор полученного блока, который должен использоваться программой для выполнения всех операций с блоком EMB. Если блок EMB программе больше не нужен, она должна освободить его с помощью функции 0Ah.
Количество блоков EMB, которое может быть заказано, определяется в командной строке драйвера HIMEM.SYS параметром /NUMHANDLES=. Значение по умолчанию - 32, максимальное значение - 128.
Регистры на входе: |
AH = 0Ah |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, 81h, A2h, B2h |
Функция освобождает блок EMB, заказанный предыдущей функцией. При этом все данные, находившиеся в блоке, будут потеряны.
Регистры на входе: |
AH = 0Bh |
Регистры на выходе: |
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.
Во время выполнения копирования разрешены прерывания.
Регистры на входе: |
AH = 0Ch |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка; |
Ошибки: |
BL = 80h, 81h, A2h, ACh, ADh |
Функция блокирует EMB и возвращает его базовый адрес как линейный 32-разрядный адрес. Для заблокированного EMB невозможно выполнить операцию копирования. Полученный линейный адрес действителен только для заблокированного EMB.
Регистры на входе: |
AH = 0Dh |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, 81h, A2h, AAh |
Функция разблокирует EMB, заблокированный при вызове предыдущей функции. Полученный от нее линейный адрес становится недействительным.
Регистры на входе: |
AH = 0Eh |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка; |
Ошибки: |
BL = 80h, 81h, A2h |
Эта функция используется для получения различной информации об используемых блоках EMB. Линейный адрес блока может быть получен с помощью фунции 0Ch.
Регистры на входе: |
AH = 0Fh |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, 81h, A0h, A1h, A2h, ABh |
Функция изменяет размер незаблокированного EMB. Если размер блока уменьшается, данные в старших адресах блока будут потеряны.
Регистры на входе: |
AH = 10h |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка; |
Ошибки: |
BL = 80h, B0h, B1h |
Эта функция позволяет программе получить дступ к блокам UMB, лежащих в пределах первого мегабайта адресного пространства. Для использования этих блоков не требуется управлять линией A20.
Если вам надо определить размер доступной области UMB, задайте при вызове этой функции DX=0FFFFh.
Регистры на входе: |
AH = 11h |
Регистры на выходе: |
AX = 0001h - если функция выполнена успешно, 0000h - если произошла ошибка |
Ошибки: |
BL = 80h, B2h |
После освобождения блока EMB данные, которые там находились, будут потеряны.
Приведем таблицу кодов ошибок, возвращаемых функциями в регистре BL:
PRIVATEКод |
Ошибка |
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 |
К сожалению, на программы, использующие область HMA, накладываются значительные ограничения. Они связаны с тем, что MS-DOS, а также BIOS не рассчитаны на работу с адресами памяти выше границы 1 Мбайт. Приведем список этих ограничений.
Приведем исходные тексты нескольких программ, демонстрирующих вызов функций программного интерфейса драйвера HIMEM.SYS. эти программы предназначены для работы в среде MS-DOS.
Первая программа с названием TESTHMA (листинг 11.1) демонстрирует проверку подключения драйвера и использование его основных функций.
Листинг 11.1. Файл testhma\testhma.asm
; ===================================================== ; Вызов основных функций API драйвера HIMEM.SYS ; ; (C) A. Frolov, 1997 ; ; E-mail: frolov@glas.apc.org ; WWW: http://www.glasnet.ru/~frolov ; or ; http://www.dials.ccas.ru/frolov ; ===================================================== @@out_ch MACRO c1,c2,c3,c4,c5,c6,c7,c8,c9,c10 mov ah,02h IRP chr,<c1,c2,c3,c4,c5,c6,c7,c8,c9,c10> IFB <chr> EXITM ENDIF mov dl,chr int 21h ENDM ENDM @@out_str MACRO mov ah,9 int 21h ENDM BEEP MACRO mov bx,0 mov ax, 0E07h int 10h ENDM .model small .STACK 100h .DATA msg DB 13,10,"HIMEM.SYS API Demo", 13, 10 DB "(C) Frolov A., 1997",13,10,13,10 DB "$" noHMM DB 13,10 DB "HIMEM.SYS not installed",13,10,"$" yesHMM DB 13,10,"HIMEM.SYS istalled, ", "$" ver1 DB "version: ", "$" ver2 DB ", modification: ", "$" errmsg DB 13,10,"Error code ", "$" okmsg DB 13,10,"Success!", "$" hmareq DB 13,10,"Request HMA", "$" hmarel DB 13,10,"Release HMA", "$" enA20 DB 13,10,"Open A20", "$" dsA20 DB 13,10,"Close A20", "$" loc_enA20 DB 13,10,"Local open A20","$" loc_dsA20 DB 13,10,"Local close A20", "$" check_A20 DB 13,10,"Check A20", "$" free_ext_mem DB 13,10,"Extended memory, Kbyte: ", "$" max_ext_block DB 13,10,"Max free Extended memory block, Kbyte: ", "$" HMMEntry dd ? .CODE begin: mov ax, DGROUP mov ds, ax 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: ; Завершаем работу программы и ; возвращаем управление операционной системе mov ax, 4C00h int 21h ; Вывод на экран содержимого регистра 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 begin
Приведем текст программы, составленная на языке программирования Си, которая вызывает функции драйвера расширенной памяти. Эта программа будет работать только в моделях памяти Small и Compact. Для других моделей памяти требуется изменить строки интерфейсного модуля hma.asm, в которых передаваемые функциям параметры извлекаются из стека и тип процедур:
Аргументы |
Small, Compact |
Large, Huge |
Первый аргумент |
[bp+4] |
[bp+6] |
Второй аргумент |
[bp+6] |
[bp+8] |
Текст программы CALLHMA вы найдете в листинге 11.2, а текст интерфейсного модуля - в листинге 11.3.
Листинг 11.2. Файл callhma\callhma.c
// ===================================================== // Работа с драйвером HIMEM.SYS // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ===================================================== #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <dos.h> #include <string.h> struct XMM_Move { unsigned long Length; unsigned short SourceHandle; unsigned long SourceOffset; unsigned short DestHandle; unsigned long DestOffset; }; extern long XMM_Installed(void); extern long XMM_Version(void); extern long XMM_RequestHMA(unsigned); extern long XMM_ReleaseHMA(void); extern long XMM_GlobalEnableA20(void); extern long XMM_GlobalDisableA20(void); extern long XMM_EnableA20(void); extern long XMM_DisableA20(void); extern long XMM_QueryA20(void); extern long XMM_QueryLargestFree(void); extern long XMM_QueryTotalFree(void); extern long XMM_AllocateExtended(unsigned); extern long XMM_FreeExtended(unsigned); extern long XMM_MoveExtended(struct XMM_Move *); extern long XMM_LockExtended(unsigned); extern long XMM_UnLockExtended(unsigned); extern long XMM_GetHandleLength(unsigned); extern long XMM_GetHandleInfo(unsigned); extern long XMM_ReallocateExtended(unsigned, unsigned); extern long XMM_RequestUMB(unsigned); extern long XMM_ReleaseUMB(unsigned); void error(char *msg, long rc); int 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("\nHIMEM.SYS installed"); ver = XMM_Version(); printf("\nver XMM: %4X,%4x", (short)ver, (short)(ver >> 16)); } else { printf("\nHIMEM.SYS not found"); exit(-1); } // Запрашиваем управление областью HMA rc = XMM_RequestHMA(0xffff); if(rc) error("Request HMA error",rc); else { // Открываем линию A20 rc = XMM_GlobalEnableA20(); if(rc) error("Open A20 error",rc); // Копируем тестовое сообщение сначала из // стандартной памяти в область HMA, // затем обратно в стандартную память FP_SEG(ptr) = 0xffff; FP_OFF(ptr) = 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("Close A20 eror",rc); rc = XMM_ReleaseHMA(); if(rc) error("Free HMA error",rc); } // Получаем блок EMB размером в 1 Кбайт handle = XMM_AllocateExtended(1); if(handle < 0) error("Request XMB error",handle); // Копируем тестовое сообщение сначала из // стандартной памяти в блок EMB, // затем обратно в стандартную память move_d.Length = strlen(testmsg) + 1; move_d.SourceHandle = 0; (char far*)move_d.SourceOffset = (char far*)testmsg; move_d.DestHandle = (short)handle; move_d.DestOffset = 0L; rc = XMM_MoveExtended(&move_d); if(rc < 0) error("Copy in EMB error",rc); move_d.Length = strlen(testmsg) + 1; move_d.DestHandle = 0; (char far*)move_d.DestOffset = (char far*)buf; move_d.SourceHandle = (short)handle; move_d.SourceOffset = 0L; rc = XMM_MoveExtended(&move_d); if(rc < 0) error("Copy from EMB error",rc); // Выводим сообщение для проверки printf("\n%s",buf); // Освобождаем блок EMB rc = XMM_FreeExtended((unsigned)handle); if(rc) error("Free XMB error",rc); return 0; } // Функция для вывода сообщения об ошибке // и кода ошибки void error(char *msg, long rc) { rc = (unsigned char)(rc >> 24) ; printf("\n%s, error: %02.2X\n", msg, (unsigned char)rc); exit(-1); } Листинг 11.3. Файл callhma\hma.asm ; ===================================================== ; Это интерфейсный модуль для вызова функций ; XMS из Си. Текст программы рассчитан на ; модель памяти Small ; ; (C) A. Frolov, 1997 ; ; E-mail: frolov@glas.apc.org ; WWW: http://www.glasnet.ru/~frolov ; or ; http://www.dials.ccas.ru/frolov ; ===================================================== .model small .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 установлен. ; ;** _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 битах ; возвращаемого значения ; ;** _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 назначена программе. ; ;** _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 освобождена. ; ;** _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 включена. ; ;** _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 выключена. ; ;** _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 включена. ; ;** _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 выключена. ; ;** _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 включена. ; ;** _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 - размер блока. ; ;** _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 - размер расширенной памяти. ; ;** _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 - младший байт содержит индекс ; полученного блока памяти. ; ;** _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 - блок освобожден. ; ;** _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 - блок скопирован успешно. ; ;** _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 - блок заблокирован, функция ; возвращает физический адрес блока ; памяти. ; ;** _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 - блок разблокирован. ; ;** _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 - длина блока в килобайтах. ; ;** _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 - младший байт - общее количество ; индексов в системе; ; старший байт - счетчик блокирования. ; ;** _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 - младший байт содержит индекс ; полученного блока памяти. ; ;** _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. ; ;** _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 освобождена. ; ;** _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