10. Расширенная память

Компьютеры 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.

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

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

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

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


10.2. Установка драйвера 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 может выдавать сообщения об ошибках в следующих случаях:

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

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

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

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

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

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

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

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


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

Для вызова драйвера программа должна получить адрес специальной управляющей программы, которая выполняет все функции по обслуживанию расширенной памяти и области 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 байтов.

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

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

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

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

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

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

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

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

Ошибки: нет.


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

Запросить область 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.

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

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

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

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


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

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

Глобальное открывание линии A20

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

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

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


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

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

Глобальное закрывание линии A20

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

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

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


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

Локальное открывание линии A20

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

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

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


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

Локальное закрывание линии A20

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

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

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


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

Определение состояния линии A20

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

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

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


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

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

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

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

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

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


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

Получить блок 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 = 0Ah;

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

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

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


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

Копирование блоков 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.

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

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

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

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

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

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

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


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

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

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

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

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

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


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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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



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

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

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

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

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

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

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

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


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

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

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

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

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

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

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



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

10.3.4. Коды ошибок

Приведем таблицу кодов ошибок, возвращаемых функциями в регистре 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

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

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

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

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

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


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

Приведем текст программы, позволяющей программам, составленным на языке программирования Си, использовать функции драйвера расширенной памяти. Эта программа будет работать только в моделях памяти 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);

}