ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



Глава 7. ИСПОЛЬЗОВАНИЕ РАСШИРЕННОЙ ПАМЯТИ
                     Урок истории
                     Менеджер расширенной памяти
                     Интерфейс прикладной программы EMS
                     Написание программ, использующих расширенную память
                     Системное программное обеспечение
                     Краткое содержание
                     Библиография
                     Программы интерфейса низкого уровня и пример приложения

              Включение спецификации расширенной памяти в MS-DOS 4.0  сде-
         лало  полноправным  данный  стандарт в качестве метода увеличения
         памяти во всех системах  MS-DOS.  Несмотря  на  все  возрастающее
         быстродействие и сложность IBM-совместимых персональных компьюте-
         ров на базе DOS,  их предельные аппаратурная и программная произ-
         водительность была подвержена ограничениям,  накладываемым струк-
         турой системы  и  архитектурой  процессора  8088,  встроенными  в
         исходный персональный компьютер IBM. До недавнего времени один из
         таких пределов,  барьер 640К,  являлся постоянным источником зат-
         руднений для пользователей и программистов систем MS-DOS. Обозна-
         чающий  максимальную  величину  ОЗУ пользователя, поддерживаемого
         MS-DOS, предел  памяти  в  640К  появлялся в качестве постоянного
         препятствия к неумолимой тенденции разрабатывать и применять  бо-
         лее мощные программные средства.
              Пользователи, требующие сложных баз данных, электронных таб-
         лиц,  графических интерфейсов и резидентных утилит,  хотят приме-
         нять эти средства одновременно под управлением многозадачной сис-
         темы с окнами.  Разработчики готовы удовлетворить такие запросы с
         помощью резидентных отладчиков,  инструкций реального  времени  и
         наборов средств интерфейса пользователя. Обе группы предпринимали
         отчаянные усилия при ограничении в 640К без определенного решения
         для тупика DOS.
              Вся промышленность персональных компьютеров вступила в  нас-
         тоящее  время  в  сотрудничество для создания работающего решения
         для преодоления предела памяти в MS-DOS по схеме расширения памя-
         ти,  именуемой расширенной памятью, которая обеспечивает програм-
         мам MS-DOS доступ максимум к 32 мегабайтам ОЗУ помимо  640К, под-
         держиваемых  MS-DOS.  К  сожалению,  расширенная  память не может
         использоваться приложениями MS-DOS автоматически. Любая программа
         должна писаться специальным образом для ее распознавания и приме-
         нения.  Хотя процесс распознавания и применения не является авто-
         матическим,  он не особенно сложный или загадочный.  Данная глава
         обеспечит Вам основы и знания,  необходимые для добавления  этого
         мощного средства к Вашей собственной базе программирования.
              Требования производительности и гибкости,  которые пользова-
         тели предъявляют к программному обеспечению,  практически диктуют
         программам прямое управление аппаратурой персонального компьютера
         в  обход DOS и BIOS для получения максимума из аппаратуры семейс-
         тва персональных компьютеров.  Создание таких программ -  сложная
         задача  для разработчиков,  которые не только должны владеть тон-
         костями аппаратуры персонального компьютера и его  различных плат
         адаптеров ввода/вывода, но также обращать специальное внимание на
         приспособление к различиям в аппаратуре установленной  базы  сис-
         тем, совместимых с IBM PC.
              К счастью,  разработчики приложений, которые используют рас-

                                      - 7-2 -
         ширенную память,  могут не вникать в неясные подробности оборудо-
         вания плат расширения памяти для получения высокой производитель-
         ности   и   гибкости.   Полный   доступ   к   расширенной  памяти
         обеспечивается хорошо документированными аппаратурно независимыми
         программными интерфейсами, которые были разработаны и поставляют-
         ся основными производителями аппаратного и программного обеспече-
         ния персонального компьютера.
              Наиболее известным из программных интерфейсов является Lotus
         /Intel/Microsoft Expanded Memory  Specification  (LIM EMS) - спе-
         цификация расширенной памяти названных  фирм.  Впечатляющее  мно-
         жество  коммерчески  доступных  систем пользуется расширенной па-
         мятью LIM EMS для  снятия  ограничения  DOS.  Windows  2.0  фирмы
         Microsoft  и DESQView фирмы Quarterdeck Office Systems используют
         расширенную память для облегчения нескольких многозадачных прило-
         жений.  Lotus 1-2-3  и Symphony,  Excel фирмы Microsoft,  AutoCAD
         фирмы Autodesk и другие популярные прикладные программы - все ис-
         пользуют  расширенную  память  для  предоставления  пользователям
         средств решения более объемных и более  сложных  задач  реального
         мира. PC-DOS и MS-DOS 4.0 содержат драйверы, которые поддерживают
         стандарт LIM EMS,  как часть операционной  системы,  хотя  ранние
         версии  DOS  4.0,  по-видимому,  не поддерживают функцию 19h EMS,
         функцию Get/Set Handle Attribute,  а первые поставленные с систе-
         мой драйверы содержали ошибки. PC-DOS и MS-DOS 4.0 используют па-
         мять LIM EMS 4.0 для буферизации секторов из открытых файлов (уп-
         равляемая  параметром  BUFFERS  в  файле  CONFIG.SYS)  и проверки
         элементов каталогов (которую команда FASTOPEN предоставляет с DOS
         3.3 и более поздних версий). Первые версии MS-DOS 4.0, видимо, не
         используют средства многозадачности LIM EMS 4.0  никоим  образом.
         (Заметьте, что во всей данной главе, всегда когда мы ссылаемся на
         версию 4.0 MS-DOS, мы также включаем и PC-DOS 4.0, если специаль-
         но не указано иное.)
              В данной главе проводится глубокий обзор методов,  необходи-
         мых  для  использования  расширенной  памяти  в Ваших собственных
         программных проектах.  Расширенная память может быть мощным, мно-
         госторонним  средством,  увеличивающим возможности Ваших программ
         для обработки более объемных задач,  для быстрого доступа к боль-
         шим базам данных по заказу,  хранящихся на диске,  для разделения
         данных с другими программами или для уменьшения памяти DOS, кото-
         рая им требуется.
              Вы можете удивиться,  когда узнаете,  что  Ваш  персональный
         компьютер   даже не нуждается в каком-либо специальном оборудова-
         нии или платах дополнительной памяти для Вас,  чтобы  записывать,
         тестировать  и выполнять приложения расширенной памяти.  В данной
         главе объясняется, каким образом программное обеспечение эмуляции
         расширенной  памяти  может  снабдить  Вас недорогим средством для
         разработки приложений расширенной памяти.
              Вы узнаете, как расширенная память подгоняется к архитектуре
         аппаратурного и программного обеспечения  персональных  компьюте-
         ров,  совместимых  с  IBM  PC.  Вы также узнаете о соглашениях по
         программированию и протоколах, которые необходимы для того, чтобы
         использовать расширенную память без вмешательства в другие прило-
         жения, включая:
              - Как определить,  когда на персональном компьютере присутс-
                твует расширенная память, и, если присутствует, то, сколь-
                ко ее установлено.
              - Как разместить,  освободить и работать с до 32 мегабайтами
                расширенной  памяти,  с  помощью интерфейса прерывания 67h

                                      - 7-3 -
                менеджера расширенной памяти,  определенного спецификацией
                расширенной памяти Lotus/Intel/Microsoft.
              - Как  эксплуатировать  функциональные  и легко используемые
                усовершенствования,  включенные  в  последнюю версию (4.0)
                LIM EMS.
              - Как  интерпретировать  и  реагировать  на  условия ошибок,
                возвращаемые подсистемой расширенной памяти.

              В данной главе мы дадим ссылочные материалы,  подробно опре-
         деляющие конкретный механизм прерываний и соглашения по использо-
         ванию регистров, требующиеся для применения интерфейса программи-
         рования  LIM  EMS.  Мы также поможем Вам определять типы структур
         данных,  которые наиболее пригодны для сохранения  в  расширенной
         памяти.
              Мы проследим историю и мотивы спецификации расширенной памя-
         ти  Lotus/Intel/Microsoft и усовершенствованной спецификации рас-
         ширенной памяти AST/Qadram/Ashton-Tate (AQA  EEMS).  Вы  увидите,
         каким  образом  различные средства этих стандартов включаются для
         способствования преодоления ограничениям памяти DOS для почти лю-
         бого типа программ, включая драйверы устройств, утилиты, остающи-
         еся резидентно в памяти после  завершения,  и  усовершенствования
         ОС.  В данной главе будут приведены соображения по технике и сов-
         местимости,  относящиеся к каждой из спецификаций расширенной па-
         мяти,  включая поддержку EMS 4.0,  встроенную в MS-DOS 4.0,  так,
         что приложения,  которые Вы пишете, будут совместимы с широчайшим
         множеством реализаций расширенной памяти.
              Для того,  чтобы Вы могли начать программировать расширенную
         память, в данной главе содержится ряд программ интерфейса низкого
         уровня,  написанных на языке С фирмы Microsoft версия 5.0, и при-
         мер  приложения.  Пример состоит из двух законченных,  работающих
         программ и дает Вам демонстрацию от начала до конца ключевых  ме-
         тодов  программирования  LIM EMS,  представленных в данной главе.
         Некоторые из сложных способов, иллюстрируемых данным приложением,
         включают  в  себя разделение данных между двумя программами и ис-
         пользование расширенной памяти в программе обслуживания  прерыва-
         ния.
              Мы начинаем рассмотрение опций расширенной памяти  с истории
         и  событий в промышленности персональных компьютеров, совместимых
         с IBM, которые вызвали ее разработку и использование.

                                   Урок истории

              В 1981 г.  типичный персональный компьютер мог адресовать не
         более 64 килобайт основной памяти.  Серьезные программисты, рабо-
         тавшие на этих машинах,  тратили неординарное количество  времени
         для  наскребывания  последних нескольких байтов,  необходимых для
         введения еще одного дополнительного  средства.  Появление  персо-
         нального  компьютера IBM с ОЗУ,  которое было на порядок величины
         больше, чем у предшественников, казалось предлагает продолжитель-
         ный перерыв в нехватке памяти.
              Едва прошло три года,  аналог закона Паркинсона  для  памяти
         ЦВМ (работа расширяется,  чтобы заполнить время, доступное для ее
         выполнения) обеспечил для IBM PC то же,  что было для всех преды-
         дущих поколений ЦВМ. Сегодня электронные таблицы, интегрированные
         приложения,  сети и поток резидентных утилит сделали пространство
         памяти IBM PC столь же заполненным и ограниченным,  как и у пред-
         шественников.

                                      - 7-4 -
              Идеального решения для предела ОЗУ не существует. Даже, хотя
         микропроцессор Intel 8088 в IBM PC поддерживает  адресное  прост-
         ранство  1  Мбайт,  384 Кбайт адресного пространства между 640К и
         пределом адресации 1 Мбайт зарезервированы для буферов видеоадап-
         теров,  системного  и  Бейсик  ПЗУ и модулей ПЗУ BIOS других плат
         ввода/вывода.  Новые приложения PC-DOS загнаны в ловушку в грани-
         цах  640 Кбайт пользовательского ОЗУ,  с которым семейство IBM PC
         было рождено.
              Одним из  традиционных  решений  проблем  с памятью является
         оверлей.  Оно часто используется приложениями персональных компь-
         ютеров,  в которых компоненты кодов программ можно разложить над-
         лежащим образом. Для других типов приложений, таких как электрон-
         ные таблицы,  данный подход эффективен не до конца.  Требования к
         памяти для данного типа приложений вызываются главным образом по-
         тенциально  неограниченным  размером их центральных структур дан-
         ных, а не размерами их выполняемых кодов.

                                      LIM EMS

              В отсутствие   универсального   решения   корпорации   Lotus
         Development,  Intel и Microsoft объединились для получения схемы,
         позволяющей индивидуальным приложениям работать за пределом 640К,
         который накладывается реализацией DOS IBM PC. Результат называет-
         ся спецификацией расширенной памяти Lotus/Intel/Microsoft или LIM
         EMS.Intel производит платы,  содержащие эту память, Lotus адапти-
         ровала свою электронную таблицу для использования этой  памяти, а
         Microsoft убедилась,  что спецификации будут отвечать требованиям
         усовершенствований ОС, над которыми ведется работа.
              LIM EMS  фактически  определяет новую реализацию популярного
         старого действия при недостатке адресного пространства:  переклю-
         чение  банков памяти.  Вкратце,  схемы переключения банков памяти
         работают с использованием  электронных  переключателей  (в  форме
         программно  адресуемых портов ввода/вывода),  которые динамически
         изменяют отображение физических блоков памяти на часть процессор-
         ного  адресного пространства.  В этом случае компьютерная система
         может обращаться к большему числу байтов физической  памяти,  чем
         обеспечивает архитектура памяти процессора,  хотя в данный момент
         времени не все байты физической памяти программе доступны.
              Как техническое  решение  проблемы пространства памяти,  LIM
         EMS вовсе не более впечатляющая или эффективная, чем схемы перек-
         лючения банков, присутствовавшие во многих ЦВМ, созданных в тече-
         ние эры микропроцессоров 6502 и 8080. Что действительно имеет LIM
         EMS, так это - поддержку нескольких лидеров рынка в промышленнос-
         ти персональных компьютеров и документацию, которая легко доступ-
         на для разработчиков программного обеспечения и не требует запро-
         са.  Это обстоятельство - весьма  большая  редкость  в  настоящее
         время, когда основные поставщики программного обеспечения, по-ви-
         димому,  сконцентрировали усилия на продаже средств создания при-
         ложений разработчикам по ценам от 500 до 3000 долларов.
              До выхода версии 4.0 PC-DOS IBM  оставалась  нейтральной  по
         отношению к LIM EMS, выбрав поддержку принятия OS/2 разработчика-
         ми приложений в качестве более устойчивого решения проблемы памя-
         ти.  В то время как OS/2 представляет решение на долгий срок, для
         большинства пользователей MS-DOS относительное запоздание ее вве-
         дения и отсутствие полной совместимости вверх с большой долей ги-
         гантского существующего оборудования и  программного  обеспечения
         ограничивают ее немедленную пригодность в качестве решения.
              Поскольку LIM EMS практична, немедленно доступна и совмести-
         ма вверх со всеми существующими системами, работающими под управ-
         лением MS-DOS, она стала коммерчески и технически успешным спосо-
         бом  для  программ DOS для преодоления барьера 640К.  Фактически,
         некоторые промышленные наблюдатели   предполагают,  что эффектив-
         ность решения LIM EMS в действительности удлинит  жизнь систем на
         основе MS-DOS на несколько лет за точку их предполагавшегося тех-
         нологического устаревания.

              LIM EMS  3.2

              Первая широко поддержанная версия LIM EMS за номером 3.2 бы-
         ла опубликована в сентябре 1985 г.   Она определила протокол рас-
         ширенной памяти, обеспечивавший надлежащим образом спроектирован-
         ные  программные  приложения  памятью  для  данных   или   кодов,
         переключаемую  банками,  объемом до 8 Мбайт.  Данная спецификация
         включала в себя средства,  которые позволяли нескольким  активным
         приложениям использовать  эту  память  одновременно без взаимного
         влияния.
              Способность для  многих  программ,  использующих расширенную
         память, сосуществовать особенно выгодна разработчикам резидентных
         программ,  по завершении остающихся в памяти,  по крайней мере, в
         двух аспектах.  Во-первых,  такая программа может  хранить  часть
         своих  данных или кода в расширенной памяти,  что уменьшает объем
         обычно  занимаемой ею памяти.  Во-вторых, конфликтов между такими
         и другими программами из-за использования расширенной памяти мож-
         но избежать,  так как LIM EMS определяет конкретные соглашения по
         программированию,  которые предотвращают появление таких конфлик-
         тов.

                          Идеи и терминология LIM EMS 3.2

              Основная схема LIM EMS 3.2 работает следующим образом:
              1. На  машине может быть установлено до 8 Мбайт ОЗУ на одной
         или нескольких платах. В отличие от многообразных плат памяти на-
         копитель  на этих платах делится на страницы по 16 Кбайт.  Расши-
         ренная память не адресуется приложениями DOS непосредственно, так
         как она не появляется в младших 640 К адресного пространства пер-
         сонального компьютера.
              2. Эти платы памяти также содержат набор регистров соответс-
         твия, которые управляются программно для установления отображения
         какой-либо  из  16-Кбайтных страниц на плате (платах) расширенной
         памяти на любую из четырех 16-Кбайтных зон  в  64-Кбайтной  части
         адресного пространства персонального компьютера, именуемого стра-
         ничным кадром. Страничный кадр размещается где-то в резервном ад-
         ресном пространстве персонального компьютера над 640К и ниже пре-
         дела адресации 8086/8 - 1  М.  Каждая  зона  в  страничном  кадре
         называется  физической  страницей  и  определяется  числом 0 - 3.
         Страничный кадр образует окно, через которое правильно написанная
         программа  может  получить  доступ  ко  всей емкости памяти платы
         (плат) расширенной памяти.  Процесс изменения содержимого регист-
         ров  отображения плат для обеспечения доступности страницы расши-
         ренной памяти программе называется страничным отображением.
              3. Управление системой расширенной памяти,  включая странич-
         ное отображение,  выполняется программной компонентой, называемой
         Менеджер  расширенной  памяти  или ЕММ (Expanded Memory Manager),
         которая поставляется изготовителем платы расширенной  памяти.  Во

                                      - 7-6 -
         многом так же,  как DOS и BIOS обеспечивают программный интерфейс
         между приложением и аппаратурой ЦВМ,  находящейся ниже него,  ме-
         неджер расширенной памяти обеспечивает программный интерфейс меж-
         ду приложением и системой расширенной памяти.  Менеджер расширен-
         ной  памяти  загружается в память так же,  как драйвер клавиатуры
         DOS  во время загрузки и сообщается с программами через программ-
         ное прерывание 67h, используя механизм передачи параметров, срав-
         нимый с интерфейсом прерывания 21h DOS.
              4. По запросу программы менеджер расширенной памяти размеща-
         ет набор из одной или более логических страниц для  программы. Он
         также размещает обработчик (handle), который программа использует
         в последующих запросах расширенной памяти к менеджеру расширенной
         памяти,  для определения набора страниц расширенной памяти, с ко-
         торыми нужно работать.  Во многом таким же образом как обработчик
         файлов DOS используется ОС для отслеживания файлов, открытых каж-
         дой программой,  обработчики расширенной памяти используются  ме-
         неджером  расширенной  памяти для отслеживания множества активных
         страниц расширенной памяти каждой программы.  Формат  обработчика
         неопределен, за исключением того факта, что он является 16-битной
         величиной.  Номера логических страниц,  связанные с обработчиком,
         отсчитываются относительно нуля до значения, на единицу меньшего,
         чем количество страниц, запрошенных программой.
              5. Когда  от менеджера расширенной памяти запрашивается обс-
         луживание, программа определяет конкретную страницу в 16К  расши-
         ренной  памяти,  которую  она желает использовать,  путем задания
         комбинации обработчика и номера логической страницы.
              Примечание: Ранние  версии  спецификации  используют  термин
         идентификатор (ID) процесса, а не термин обработчик для ссылки на
         множество страниц расширенной памяти. Разработчики LIM EMS внача-
         ле предполагали, что каждая программа будет размещать только одно
         множество страниц,  подразумевая соответствие один к одному между
         программами и идентификаторами процессов.  На  практике,  однако,
         многие  программы  запрашивают более одного набора страниц расши-
         ренной памяти. Как Вы увидите позже, это законный и полезный при-
         ем программирования.  Однако,  факт,  что одна программа могла бы
         обладать более  чем одним идентификатором процесса EMS, запутывал
         многих пользователей, что и подсказало изменение терминологии.
              6. Менеджер расширенной памяти LIM EMS 3.2 системы расширен-
         ной  памяти  обеспечивает  14  функций,  вызываемых пользователем
         (плюс две резервные функции),  которые приложения используют  для
         получения  информации  о  страницах расширенной памяти и работы с
         ними.

                    Улучшенная спецификация расширенной памяти

              Вскоре сформировалось второе объединение в компьютерной про-
         мышленности  для  продвижения улучшенной спецификации расширенной
         памяти. Результатом его усилий, которое поддерживалось корпораци-
         ями AST Research, Quadram и Ashton-Tate была вверх совместимая (с
         LIM EMS 3.2) схема расширенной памяти, именуемая AQA EEMS.

                             Ограничение размера окна
              Основное ограничение  схемы  LIM  EMS 3.2 с точки зрения AQA
         заключалось в ничтожном размере окна страничного кадра, равном 64
         Кбайт.  Через такое небольшое окно в расширенную память программа
         может иметь доступ только к 4 16-Кбайтным  страницам  расширенной
         памяти из 512 возможных (8 мегабайт), поддерживаемых спецификаци-

                                      - 7-7 -
         ей.  AQA EEMS позволяет программе обращаться,  по крайней мере  в
         теории,  к  максимум  64 16-Кбайтных страниц расширенной памяти в
         любой данный момент времени. Быстрый подсчет (думающие в двоичном
         коде могут сдвигать биты) показывает,  что при этом память,  при-
         годная для отображения должна составлять до 1 Мбайта!  Но  подож-
         дите,  спросите Вы, как там насчет всех видеобуферов и ПЗУ BIOS в
         пространстве над 640К, не говоря о DOS и приложениях в пространс-
         тве ниже 640К? Учтено ли это в регистрах отображения?
              Нет. Действительность,  с которой имеет дело EEMS та  же,  с
         которой  обращается LIM EMS;  AQA EEMS просто работает с этой ре-
         альностью более смело.  LIM EMS 3.2 - пример консервативной в са-
         мом худшем смысле философии проектирования. Например, разработчи-
         ки определили,  что персональный компьютер с  платой  улучшенного
         графического адаптера (EGA) и сетевой платой, содержащей ПЗУ, мо-
         жет обладать только 64 К неиспользуемого  адресного  пространства
         над  640К.  Адресным пространством ниже 640К уже твердо управляет
         DOS.  Таким  образом,  для  разработчиков  LIM  EMS  3.2  остался
         64-Кбайтный страничный кадр.
              Разработчики AQA оценивали задачу с другой точки зрения. Они
         рассуждали,  что стандартный персональный компьютер с CGA или MDA
         обладает достаточным адресным пространством выше 640К для отобра-
         жения,  по  крайней  мере,  12 страниц EMS одновременно.  И фирма
         Quarterdeck Office Systems,  не уступая ничего  DOS,  разработала
         расширитель ОС, называемый DESQView, который может менять местами
         программы ниже и выше 640К.  Если бы DESKView была способна  под-
         ставлять почти мгновенную операцию отображения страниц вместо от-
         носительно медленного процесса обмена местами (своппинга), ее по-
         тенциал  в  качестве высокопроизводительной многозадачной системы
         для IBM PC сильно возрос бы.
              Фирма AST разработала плату,  способную к отображению памяти
         через  адресное  пространство  1  Мбайт,  снабдив  таким  образом
         DESKView механизмом,  необходимым для быстрого переключения между
         программами. Практические ограничения, однако, препятствуют пере-
         отображение тех частей адресного пространства, которые занимаются
         ПЗУ BIOS, дисплейным адаптером и памятью на основной плате, необ-
         ходимой для загрузки системы.
              Схема EEMS на рынке пользуется ограниченным успехом. Сочета-
         ние путаницы в продаже продукции,  вызванной AST и Quadram, и вя-
         лой поддержки ее функциональных расширений другими основными раз-
         работчиками  программного  обеспечения  персонального  компьютера
         ограничили ее рост в качестве главного  альтернативного стандарта
         для LIM EMS. Разработчики были довольны, что EEMS, по крайней ме-
         ре,  совместима вверх с LIM EMS,  но многие из них,  очевидно, не
         думают, что данные расширения, которые она предлагает, стоят  за-
         трат, если расширения влекут за собой потерю  совместимости  вниз
         со схемой LIM.

                                    LIM EMS 4.0

              Вне зависимости  от  результатов  рынка  кто-то в лагере LIM
         должен был найти в AQA EEMS что-либо  привлекательное.  Поскольку
         приближался  к  выпуску  продукт фирмы Microsoft - Windows 2.0 (а
         величина памяти, требовавшаяся для работы с ним, все росла), идея
         получить возможность переключать задачи быстро в памяти ниже 640К
         должна была приобрести заметную привлекательность. И  может быть,
         пространство  над 640К не было заполнено целиком,  поскольку даже
         IBM PS/2 с контроллером дисплея VGA  имел  пространство  для,  по

                                      - 7-8 -
         крайней мере, шести 16-Кбайтных страниц.
              В августе 1987 г. группа Lotus/Intel/Microsoft объявила спе-
         цификацию LIM EMS 4.0.  Новая спецификация включила в себя в сущ-
         ности все улучшенные свойства AQA EEMS и добавила  еще  несколько
         собственных,  которые будут рассмотрены подробно позднее в данной
         главе.  Группа AQA получила столь сильное впечатление (или сомне-
         валась  в мудрости предпринятия еще одной схватки на рынке),  что
         вскоре объявила о своей публичной поддержке спецификации 4.0.
              В 1988 г. IBM и Microsoft включили драйверы EMS в версию 4.0
         операционных систем PC-DOS и MS-DOS. Драйверы EMS в ранних выпус-
         ках  содержали  ошибки  и технические специалисты явно определили
         отсутствие поддержки для функции 19h EMS,  функции "Получения/Ус-
         тановки   атрибута  обработчика",  хотя во всех других отношениях
         реализация EMS DOS 4.0 соответствует спецификации  LIM  EMS  4.0.
         Данная  глава обращается к спецификации LIM EMS 4.0 независимо от
         того,  есть ли это в операционной системе DOS 4.0,  поскольку  во
         всех отношениях сопряжение с системами EMS 4.0 идентично.
              Как и в 1988 г.  все основные участники в области MS-DOS ос-
         тановились на спецификации LIM EMS 4.0. Унификация стандарта рас-
         ширенной памяти снимает, по крайней мере, одну заботу с умов раз-
         работчиков программного обеспечения, которым больше не приходится
         беспокоиться, что же лучше - быть в безопасности с EMS или пофан-
         тазировать с расширениями EEMS.
              На рис.  7-1 показано, каким образом до 32 Мбайт расширенной
         памяти  адресуются в пределах двух различных областей 1-мегабайт-
         ного адресного пространства персонального компьютера IBM. В зави-
         симости  от использования ПЗУ BIOS и адаптером дисплея пространс-
         тва между 640К и 1024К,  от четырех  до  12  страниц  по  16Кбайт
         расширенной памяти могут отображаться на эту область. Пригодная к
         отображению обычная память (черта AQA EEMS и LIM EMS  4.0)  может
         применяться только расширениями операционной системы.
       ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
       і Некоторые свойства новой спецификации не были четко задокументи-і
       ірованы в издании 1987 г.   Пара пересмотренных граничных  условийі
       ідля  некоторых из функций версии 3.2 вводила в созданную специфи-і
       ікацию 4.0 несовместимость  вниз  между  версиями.  Пересмотреннаяі
       іспецификация 4.0, прояснившая новые свойства и устранившая несов-і
       іместимость была опубликована в октябре 1987 г.                   і
       АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
              Количество функций,  поддерживаемых спецификацией 4.0, вдвое
         - до 28 - повысило число функций,  вызываемых пользователем,  до-
         ступных в спецификации 3.2. Большинство из новых функций обладают
         несколькими подфункциями. Таким образом, весь предмет расширенной
         памяти теперь в несколько раз больше,  чем был. Официальный доку-
         мент  спецификации  для  LIM EMS 4.0,  внешность которого следует
         стилю Технического справочного руководства DOS, более чем удвоил-
         ся по объему по отношению к своему предшественнику версии 3.2.
              В то время как в данной главе будет представлено  так  много
         подробностей, насколько это возможно, для иллюстрации идей расши-
         ренной памяти,  серьезные разработчики могут обнаружить,  что она
         не  может служить в качестве полной замены официальной специфика-
         ции.  Вы можете получить копию  спецификации  непосредственно  от
         Intel,  позвонив (800) 538-3373 в США и Канаде или (503) 629-7354
         везде в других местах.
              Один из возможных подходов к изучению EMS мог бы заключаться
         в изучении функций, совместимых с LIM EMS 3.2, и затем в рассмот-
         рении функций,  добавленных спецификацией 4.0. Как Вы вскоре уви-

                                      - 7-9 -
         дите,  функции,  добавленные EMS 4.0,  являются более,  чем  верх
         совместимыми расширениями:  во многих случаях новые функции пред-
         лагают намного более простые способы выполнения  задач управления
         расширенной  памятью,  чем  те,  которые были возможны со старыми
         функциями. Таким образом, в данной главе функции версий 3.2 и 4.0
         представляются совместно.

                 LIM EMS 4.0 по сравнению с LIM EMS 3.2 и AQA EEMS

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

                           Соображения по совместимости

              Если Вы намереваетесь писать приложение  с  расширенной  па-
         мятью, которое может использовать самую большую установленную ба-
         зу систем расширенной памяти,  то Вы, вероятно, не ошибетесь, ис-
         пользуя  только те функции менеджера расширенной памяти,  которые
         поддерживаются спецификацией LIM EMS 3.2.  Однако, Вы должны учи-
         тывать,  что  большинство  основных  плат расширения и поставщики
         программного обеспечения весьма одобрили спецификацию LIM EMS 4.0
         вскоре  после  ее  появления.  Такая степень принятия LIM EMS 4.0
         практически гарантирует ее положение в качестве главного стандар-
         та расширенной памяти. Следующие факторы поддерживают это утверж-
         дение:
              - Пользователю не приходится покупать никакое новое оборудо-
         вание для использования приложений, которые написаны для специфи-
         кации LIM EMS 4.0. Более старые платы расширенной памяти, спроек-
         тированные для спецификации LIM  EMS  3.2,  могут  поддерживаться
         спецификацией 4.0 - изготовителю приходится только написать новый
         менеджер расширенной памяти,  для реализации вызовов функций 4.0.
              - Корпорация  Intel снабжает владельцев своих вышеупомянутых
         плат расширенной памяти менеджером расширенной памяти,  поддержи-
         вающим спецификацию 4.0 бесплатно.
              - Практически любое новое оборудование расширенной памяти (и
         эмулятор расширенной памяти) поддерживают LIM EMS 4.0.
              - Программные продукты высокой видимости,  такие как Windows
         2.0 фирмы Microsoft (менеджер представления),  Excel (электронная
         таблица)  и DESKView 2.0 фирмы Quarterdeck Office Systems (много-
         задачная среда) - все используют средства LIM EMS  4.0.  Грядущие
         усовершенствования  электронных таблиц и баз данных других основ-
         ных  поставщиков,   ожидается,  также  будут  обладать поддержкой
         LIM EMS 4.0.
              Так как соразработчики спецификации AQA EEMS  объявили,  что
         их  новые  продукты  расширенной  памяти будут согласовываться со
         спецификацией LIM EMS 4.0, кажется, не следует давать совет прог-

                                      - 7-10 -
         раммистам  применять  функции  ЕЕМS  в новых программах.  Поэтому
          ЪДДДДДДДДДДДДДї10000000H(16Mb) Ъ Д Д Д Д ДЪДДДДДДДДї
          і     1       і                і        / і        і
          і             і                і      /   і        і
          і/\/\/\/\/\/\/                 і    /     і        і
           /\/\/\/\/\/\/\                і  /       і        і
          і             і                і          і        і
          АДДДДДДДДДДДДДЩ              / і          і        і
          ЪДДДДДДДДДДДДДї100000H(1Mb)/   і          і        і
          і      2      і          /     і          і        і
          ГДДДДДДДДДДДДДґF000H(960Kb)    і          і        і
          і     /\  2a  і      /         і          і        і
          ГДДДДДґГДДДДДДґE0000H(896Kb)   і          і        і
          і     іАДДДДДДБДДДДДЩ          і          і        і
          і  3  іЪДДДДДДВДДДДДї   6      і          і   7    і
          і     \/      і     і          і          і        і
          ГДДДДДДДДДДДДДґC0000H\         і          і        і
          і      4      і(768Kb) \       і          і        і
          ГДДДДДДДДДДДДДґB0000H    \     і          і        і
          і      4a     і(704Kb)     \   і          і        і
          АДДДДДДДДДДДДДЩ              \ і          і        і
                                         і          і        і
          ЪДДДДДВВДДДДДДїA0000H(640Kb)   і \        і        і
          і     іАДДДДДДДДДДДДДДДДДДДДДДДЩ   \      і        і
          і     іЪДДДДДДДДДДДДДДДДДДДДДДДї  8  \    і        і
          Г Д Д \/Д Д Д ґ40000H(256Kb)   і       \  і        і
          і     5       і                і         \і        і
           \/\/\/\/\/\/\і                А Д Д Д Д ДАДДДДДДДДЩ
          і\/\/\/\/\/\/\
          і             і
          АДДДДДДДДДДДДДЩ0000H(0Kb)

            Рис.7-1. Расширенная память и адресное пространство IBM PC

         1 - расширенная память (AT, PS/2 с процессорами 80286/80386),
         2 - PC/XT/AT PS/2 ROM-BIOS,
         2a- PC/AT PS/2 ROM-BIOS,
         3 - ROM дополнительных адаптеров ввода-вывода,
         4 - EGA/VGA/MDA/CGA дисплейный буфер,
         4a- EGA/VGA дисплейный буфер,
         5 - память пользователя,
         6 - страничный кадр LIM EMS, от 4 до 12 16-Кбайтных физических
             страниц,
         7 - до 32 Мбайт расширенной памяти,  от 0 до 2048 16-Кбайтных
             логических страниц,
         8 - пригодная  к отображению обычная память (для использования
             только операционной системой), 0-24 16-Кбайтных физических
             страниц

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



                                      - 7-11 -
                              Технические соображения
              С точки  зрения разработчика приложения решение о применении
         функций LIM EMS 4.0 в программе, не ограничиваясь только функция-
         ми  LIM EMS 3.2,  вначале может выглядеть как вопрос предпочтения
         программиста.
              Это справедливо  на  одном уровне,  так как возможно для Вас
         выполнить любую разумную прикладную функцию,  требующую расширен-
         ной памяти, с помощью функций, которые доступны только в специфи-
         кации LIM EMS 3.2. Однако, используя функции более высокого уров-
         ня,  введенные  спецификацией  4.0,  Вы будете способны сократить
         объем кода,  который Вам придется написать для выполнения  многих
         обычных задач при работе с расширенной памятью. В частности, пос-
         ледние разделы данной главы покажут Вам,  как функции LIM EMS 4.0
         позволяют  передавать  большие  блоки  памяти между расширенной и
         обычной памятью и выполнять код в расширенной памяти одним  вызо-
         вом функции менеджера расширенной памяти.

                            Менеджер расширенной памяти

              Многие программисты  и пользователи персональных компьютеров
         связывают расширенную память только с набором БИС  памяти,  уста-
         новленных  на  плате памяти специального типа.  Это слишком узкое
         представление неудачно,  поскольку никакая часть LIM EMS не опре-
         деляет  чего-либо  об  оборудовании,  используемом для реализации
         системы расширенной памяти. Как было кратко упомянуто во введении
         к главе,  возможно обладать расширенной памятью на компьютере во-
         обще без всякого специального оборудования. Мы приведем обоснова-
         ние  данного  смелого заявления в последнем разделе данной главы,
         описывающем несколько реализаций расширенной памяти.
              Вне зависимости  от  конструкции  системы расширенной памяти
         каждый может включать программную компоненту,  именуемую менеджер
         расширенной  памяти,  который поддерживает программный интерфейс,
         определенный спецификацией расширенной памяти,  между  прикладной
         программой и лежащей ниже системой расширенной памяти.
              Программа менеджера расширенной  памяти  сама  упаковывается
         внутри  драйвера  символьного  устройства  DOS,  определенного  в
         CONFIG.SYS,  который загружается и активизируется  DOS  во  время
         загрузки. Она отличается от других драйверов устройств DOS в том,
         что сообщение между приложением и драйвером  устройства менеджера
         расширенной  памяти не проходит через файловую систему DOS в виде
         открытий, закрытий, чтений или записей. Скорее она использует ме-
         ханизм прерываний, весьма сходный с используемым DOS, в котором в
         регистрах передаются коды функций,  параметры и коды возврата на-
         зад и вперед. Основная причина того, что менеджер расширенной па-
         мяти упакован как драйвер устройства,  заключается в  том,  чтобы
         позволить  ему  загружаться  достаточно рано в процессе загрузки,
         так чтобы драйверы устройств (например,  диски на ОЗУ и устройств
         подкачки при печати) могли использовать расширенную память.

                       Функции менеджера расширенной памяти

              Для удовлетворения  спецификации LIM EMS 4.0 менеджер расши-
         ренной памяти должен реализовывать 28 разных вызываемых пользова-
         телем функций, у многих из которых есть много подфункций. Большое
         количество  функций  и  подфункций,  определенных  спецификацией,
         представляет  значительное  препятствие к разумному использованию
         расширенной памяти.

                                      - 7-12 -
              Сложность LIM  EMS 4.0 почти требует от разработчика понима-
         ния высшего уровня функций менеджера расширенной  памяти.  Прежде
         чем  мы  начнем копаться в механике использования этих функций из
         программ на ассемблере и языках высокого уровня, мы начнем подхо-
         дить  к  данной  задаче с разбиения функций менеджера расширенной
         памяти на пять категорий:
             Информационные      Возвращают  состояние  менеджера  расши-
                                 ренной памяти, а также количества ресур-
                                 сов расширенной памяти,  которые доступ-
                                 ны и которые используются приложениями
                                 спецификации расширенной памяти.
             Управление данными  Управляют размещением, освобождением, пе-
                                 редвижением,  отображением и разделением
                                 данных и кодов в расширенной памяти.
             Управление          Управляют сохранением и восстановлением
                                 контекстов состояния отображения менедже-
                                 ра  расширенной  памяти  драйверами  уст-
                                 ройств и  программами, резидентными в па-
                                 мяти после завершения.
             Ориентированные     Управляют переключением состояния отобра-
             на операционную     жения менеджера расширенной памяти между
             систему             несколькими,одновременно работающими при-
                                 ложениями при многозадачных средах,  та-
                                 ких   как    Microsoft    Windows    или
                                 Quarterdeck DeskView.
             Зарезервированные   Услуги, зависящие от аппаратуры, которые
                                 были удалены из документированной специ-
                                 фикации,когда была объявлена LIM EMS 3.2.

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

                                                         Таблица 7-1
                             Функции LIM EMS 3.2 и 4.0
       ДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДД
                     і   Функции EMS 3.2       і  Дополнения EMS 4.0
       ДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДД
       Информационныеі (1)Получить состояние   і(21)Получить каталог об-
                     і                         і    работчика
                     і (2)Получить адрес стра- і(25)Получить массив отоб-
                     і    ничного буфера       і    ражаемых физических
                     і (3)Получить отсчет не-  і    адресов
                     і    размещенной страницы і
                     і (7)Получить версию      і
                     і(12)Получить отсчет обра-і
                     і    ботчика              і
                     і(13)Получить страницы об-і
                     і    работчика            і
                     і(14)Получить все страницыі
                     і    обработчика          і
                     і                         і
       Управление    і(4)Разместить страницы   і(17)Отображать/Не отобра-
         данными     і(5)Отображать/Не отобра- і    жать много страниц об-
                     і   жать страницу обработ-і    работчика
                     і   чика                  і(18)Переразместить страницы

                                      - 7-13 -
       ДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДД
                     і(6)Освободить страницы   і(19)Получить/Установить
                     і                         і    аттрибуты обработчика
                     і                         і(20)Получить/Установить
                     і                         і    имя обработчика
                     і                         і(22)Изменить отображение
                     і                         і    страницы и перейти
                     і                         і(23)Изменить отображение
                     і                         і    страницы и вызвать
                     і                         і(24)Передвинуть/Обменять
                     і                         і    область памяти
                     і                         і
       Управление    і(8)Сохранить отображе-   і(16)Получить/Установить
       контекстом    і   ние страницы          і    частичное отображение
                     і(9)Восстановить отобра-  і    страницы
                     і   жение страницы        і(23)Изменить отображение
                     і(15)Получить/Установить  і    страницы и вызвать
                     і   отображение страницы  і(24)Передвинуть/Обменять
                     і                         і    область памяти
                     і                         і
       Ориентиро-    і   Нет                   і(26)Получить данные об
       ванные на     і                         і    оборудовании расширен-
       операционную  і                         і    ной памяти
       систему       і                         і(27)Разместить стандарт-
                     і                         і    ные/необработанные
                     і                         і    страницы
                     і                         і(28)Сменить набор регист-
                     і                         і    ров отображения
                     і                         і(29)Подготовить аппаратуру
                     і                         і    расширенной памяти для
                     і                         і    загрузки из памяти
                     і                         і(30)Разрешить/Запретить
                     і                         і    функции набора Опера-
                     і                         і    ционной системы/Среды
       Зарезерви-    і(10)Зарезервирована      і
       рованные      і(11)Зарезервирована      і
       ДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДД

                                                         Таблица 7-2
                       Функции менеджера расширенной памяти
       ДДДВДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДД
        N і  Наименование  і   Входные регистры    і Выходные регистры
       ДДДЕДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДД
        1 іПолучить состо- і AH: 40h (код функции) і Нет
          іяние            і                       і
        2 іПолучить адрес  і AH: 41h (код функции) і BX - адрес сегмента
          ісегмента стра-  і                       і страничного кадра
          іничного кадра   і                       і
        3 іПолучить счетчикі AH: 42h (код функции) і BX - кол-во нераз-
          інеразмещенных   і                       і мещенных страниц
          істраниц         і                       і
        4 іРазместить      і AH: 43h (код функции) і DX - обработчик
          істраницы        і BX: номера страниц дляі
          і                і размещения            і
        5 іОтобразить/     і AH: 44h (код функции) і Нет
          існять отобра-   і AL: номер физической  і
          іжение страниц   і     страницы          і

                                      - 7-14 -
          іобработчика     і BX: номер логической  і
          і                і     страницы          і
          і                і DX: обработчик        і
        6 іОсвободить      і AH: 45h (код функции) і Нет
          істраницы        і DX: обработчик        і
        7 іПолучить версию і AH: 46h (код функции) і AL: двоично-дес.код
          і                і                       і версии менеджера рас-
          і                і                       і ширенной памяти
        8 іСохранить отоб- і AH: 47h (код функции) і Нет
          іражение страниц і DX: обработчик        і
        9 іВосстановить    і AH: 48h (код функции) і Нет
          іотображение     і                       і
          істраниц         і                       і
       10 іЗарезервировано і                       і
       11 іЗарезервировано і                       і
       12 іПолучить счетчикі AH: 4Вh (код функции) і BX - кол-во обработ-
          іобработчика     і                       і чиков в использовании
       13 іПолучить страни-і AH: 4Сh (код функции) і BX - кол-во страниц,
          іцы обработчика  і DX: обработчик        і размещенных для ука-
          і                і                       і занного обработчика
       14 іПолучить все    і AH: 4Dh (код функции) і BX - кол-во использу-
          істраницы обра-  і ES:DI: массив страниц і емых обработчиков
          іботчика         і        обработчика    і
       15 іПолучить отобра-і AX: 4Е00h (код функции) Массив отображения
          іжение страниц   і ES:DI: массив отображе- страниц назначения<--
          і                і ния страниц назначенияі состояние отображения
          і                і                       і менеджера расширенной
          і                і                       і памяти
          іУстановить отоб-і AX: 4Е01h (код функции) Состояние отображения
          іражение страниц і ES:DI: массив отображе- менеджера расширенной
          і                і ния страниц-источникові памяти<--массив отоб-
          і                і                       і ражения страниц-ис-
          і                і                       і точников
          іПолучить и уста-і AX: 4Е02h (код функции) Массив отображения
          іновить отображе-і ES:DI: массив отображе- страниц назначения<--
          іние страниц     і ния страниц назначенияі состояние отображения
          і                і DS:SI: массив отображе- менеджера расширенной
          і                і ния страниц-источникові памяти
          і                і                       і Состояние отображения
          і                і                       і менеджера расширенной
          і                і                       і памяти<--массив отоб-
          і                і                       і ражения страниц-ис-
          і                і                       і точников
       15 іПолучить размер і AX: 4Е03h (код функции) AL: кол-во байтов,
          імассива отобра- і                       і требуемых для массива
          іжения страниц   і                       і отображения страниц-
          і                і                       і источников или страниц
          і                і                       і назначения
       16 іПолучить частич-і AX: 4F00h (код функции) Массив частичного
          іное отображение і DS:SI-->массив отоб-  і отображения страниц
          істраниц         і ражаемых сегментов    і назначения<--частич-
          і                і ES:DI-->массив частич-і ное состояние отобра-
          і                і ного отображения стра-і жения менеджера расши-
          і                і ниц назначения        і ренной памяти
          і                і                       і
          іСтруктура масси-і                       і
          іва отображаемых і                       і

                                      - 7-15 -
          ісегментов:      і                       і
          і                                        і
          іmappable_seg_count    dw ?              і Кол-во отображаемых
          і                                        і сегм. для сохранения
          іmappable_seg_addr dw (mappable_seg_count) Адрес сегмента отоб-
          і                  dup (?)               і ражаемых сегментов
          і                                        і для сохранения
          іУстановить час- і AX: 4F01h (код функции) Частичное состояние
          ітичное отобра-  і DS:SI-->частичное отоб- отображения менеджера
          іжение страниц   і ражение страниц-источ-і расширенной памяти<--
          і                і ников                 і массив частичного
          і                і                       і отображения страниц-
          і                і                       і источников
          іПолучить размер і AX: 4F02h (код функции) AL: кол-во байтов,
          імассива частич- і BX:кол-во страниц в   і необходимое для за-
          іного отображе-  і массиве частичного    і поминания массива
          іния страниц     і отображения страниц   і частичного отображе-
          і                і                       і ния страниц с указан-
          і                і                       і ным кол-вом физичес-
          і                і                       і ких страниц
       17 іОтобразить/     і AH: 50h (код функции) і Нет
          іСнять отоб-     і AL: 00h - физическая  і
          іражение страниц і страница, заданная    і
          імногих обработ- і своим номером         і
          ічиков           і 01h - физическая стра-і
          і                і ница, заданная адресомі
          і                і сегмента              і
          і                і DX: обработчик        і
          і                і CX: кол-во элементов ві
          і                і массиве отображения   і
          і                і логических страниц в  і
          і                і физические            і
          і                і DS:SI-->массив отобра-і
          і                і жения логических страниц
          і                і в физические          і
          іСтруктура масси-і                       і
          іва отображения  і                       і
          ілогических стра-і                       і
          іниц в физическиеі                       і
          іlog_page_number   dw ?                  і Номер логич.страницы
          іphys_page_number  dw ?                  і Номер физич.страницы
          і                                        і или адрес сегмента, в
          і                і                       і зависимости от значе-
          і                і                       і ния, заданного в AL
       18 іПереразместить  іAH:51h (код функции)   і BX: номера страниц,
          істраницы        іDX: обработчик         і размещаемых для обра-
          і                іBX: номера страниц,    і ботки после перераз-
          і                ікоторые должны быть у  і мещения
          і                іобработчика после пе-  і
          і                іреразмещения           і
       19 іПолучить  атри- іAX:5200h(код функции)  і AL: 0 - обработчик
          ібуты обработчи- іDX: обработчик         і изменчивый
          іка              і                       і 1 - неизменчивый
          іУстановить ат-  іAX:5201h(код функции)  і Нет
          ірибуты  обра-   іDX: обработчик         і
          іботчика         іBL: новые  атрибуты    і
          і                іобработчика            і

                                      - 7-16 -
          і                і00h, изменчивый        і
          і                і01h, неизменчивый      і
          іПолучить воз-   іAX:5202h(код функции)  і AL: 0 - неизменчи-
          іможности по     і                       і вость не поддержива-
          іатрибутам       і                       і ется
          і                і                       і 1 - поддерживается
       20 іПолучить имя    іAX:5300h(код функции)  і Приемный буфер имени
          іобработчика     іDX:обработчик          і обработчика<--имя
          і                іЕS:DI-->8-символьный   і обработчика
          і                ібуфер-приемник имени   і
          і                іобработчика            і
          і                і                       і
          іУстановить имя  іAX:5301h(код функции)  і Буфер-источник имени
          іобработчика     іDX:обработчик          і обработчика-->имя
          і                іDS:SI-->8-символьный   і обработчика
          і                ібуфер-источник имени   і
          і                іобработчика            і
       21 іПолучить ката-  іAX:5400h(код функции)  і AL:кол-во элементов в
          ілог обработчика іES:DI-->массив катало- і массиве каталога обра-
          і                іга обработчика         і ботчика
          іКаталог обработчика:                    і
          іhandle_value     dw ?                   і Активный обработчик
          іhandle_name      db 8 dup (?)           і Имя обработчика
          іПоиск поимено-  іAX:5401h(код функции)  і DX: обработчик с ука-
          іванного обра-   іDS:SI-->8-символьный   і занным именем
          іботчика         ібуфер поиска имени     і
          і                іобработчика            і
          іПолучить общее  іAX:5402h(код функции)  і BX:общее кол-во
          ікол-во обработ- і                       і обработчиков, под-
          ічиков           і                       і держживаемых менедже-
          і                і                       і ром расширенной памяти
       22 іИзменить отоб-  іAH:55h (код функции)   і Нет
          іражение страниц іAL:0 - физические      і
          іи перейти       істраницы, определенные і
          і                іфизическими номерами   і
          і                істраниц                і
          і                і1 - физические страницыі
          і                іопределенные адресом   і
          і                ісегмента               і
          і                іDX: обработчик         і
          і                іDS:SI-->структура отобра-
          і                іжения и перехода       і
          іСтруктура отоб- і                       і
          іражения и перехода                      і
          іtarget_address   dd ?                   і Точка входа цели
          і                і                       і
          іlog_phys_map_len db ?                   і Кол-во элементов в
          і                                        і структуре отображения
          і                і                       і страниц-->структуру
          і                і                       і массива отображения
          і                і                       і логических страниц
          і                і                       і в физические, как в
          і                і                       і функции 17
          log_phys_map_ptr  dd ?                   і
       23 іИзменить отобра- AH:56h (код функции)   і Нет
          іжение страниц и іAL:0 - физические      і 1 - физические страни-
          івызвать         істраницы, определенные і цы, определенные ад-

                                      - 7-17 -
          і                іфизическими номерами   і ресом сегмента
          і                істраниц                і DX: обработчик
          і                і                       і DS:SI-->структура
          і                і                       і отображения и вызова
          іСтруктура отобраі                       і
          іжения и вызова  і                       і
          іtarget_address   dd ?                   і Удаленный-->точку
          і                і                       і входа цели
         new_page_map_len   db ?                   і Кол-во страниц для
          і                і                       і отображения после
          і                і                       і удаленного вызова
          і                і                       і -->как в функции 17
         new_page_map_ptr   dd ?                   і
         old_page_map_len   db ?                   і Кол-во страниц для
          і                і                       і отображения после
          і                і                       і удаленного возврата
          і                і                       і -->как в функции 17
         old_page_map_ptr   dd ?                   і
          іЗарезервировано  dw 4 dup (?)           і Зарезервировано для
          і                і                       і менеджера  расширен-
          і                і                       і ной памяти
          і                і                       і
          іПолучить размер іAX:5602h(код функции)  і BX:кол-во байт про-
          іпространства    і                       і странства стека,
          істека отображе- і                       і требуемое функцией:
          іния страниц     і                       і Изменить отображение
          і                і                       і страниц и вызвать
       24 іПередвинуть об- іAX:5700h(код функции)  і Нет
          іласть памяти    іDS:SI-->дескриптор об- і
          і                іласти источника/прием- і
          і                іника                   і
          іОбменять об-    іAX:5701h(код функции)  і Нет
          іласть памяти    іDS:SI-->дескриптор об- і
          і                іласти источника/прием- і
          і                іника                   і
          іДескриптор об-  і                       і
          іласти источни-  і                       і
          іка/приемника    і                       і
          іregion_length    dd ?                   і Кол-во байтов для
          і                і                       і движения/обмена
        source_memory_type  db ?                   і Обычная память: 0
          іsource_handle    dw ?                   і Обычная память: 0
          і                і                       і Расширенная память:
          і                і                       і обработчик источника
        source_init_offset  dw ?                   і Обычная память:
          і                і                       і начальное смещение в
          і                і                       і сегменте источника
          і                і                       і Расширенная память:
          і                і                       і начальное смещение в
          і                і                       і странице источника
        source_page_seg     dw ?                   і Обычная память:
          і                і                       і начальный сегмент
          і                і                       і источника
          і                і                       і Расширенная память:
          і                і                       і начальная логическая
          і                і                       і страница источника
        dest_memory_type    db ?                   і Обычная память: 0

                                      - 7-18 -
          і                                        і Расширенная память:1
          і  dest_handle   іdw ?                   і Обычная память: 0
          і                і                       і Расширенная память:
          і                і                       і обработчик приемника
          dest_init_offset  dw ?                   і Обычная память: на-
          і                і                       і чальное смещение в
          і                і                       і сегменте приемника
          і                і                       і Расширенная память:
          і                і                       і начальное смещение в
          і                і                       і странице приемника
          dest_seg_page     dw ?                   і Обычная память: на-
          і                і                       і чальный сегмент при-
          і                і                       і емника
          і                і                       і Расширенная память:
          і                і                       і начальная логическая
          і                і                       і страница приемника
       25 іПолучить массив іAX:5800h(код функции)  і CX: кол-во элементов
          іотображаемых    іES:DI-->массив отоб-   і в массиве отобража-
          іфизических      іражаемых физических    і емых физических ад-
          іадресов         іадресов                і ресов
          іМассив отобра-  і(Массив, отсортирован- і
          іжаемых физичес- іный по порядку нарас-  і
          іких адресов     ітания сегментов)       і
         phys_page_segment  dw ?                   і Адрес сегмента отоб-
          і                і                       і ражаемой страницы,
          і                і                       і соответствующей но-
          і                і                       і меру физической
          і                і                       і страницы
         phys_page_number   dw ?                   і
          іПолучить счетчикіAX:5801(код функции)   і CX: кол-во элементов
          іэлементов масси-і                       і в массиве отображае-
          іва физических   і                       і мых физических адре-
          іадресов         і                       і сов
          і                і                       і
       26 іПолучить массив іAX:5900(код функции)   і Массив конфигурации
          іконфигурации    іES:DI-->массив конфи-  і оборудования<--дан-
          іоборудования    ігурации оборудования   і ные оборудования
          іМассив конфигу- і                       і
          ірации оборудования                      і
          іraw_page_size    dw ?                   і Исходный размер
          і                і                       і страницы в байтах
         alternate_reg_sets dw ?                   і Кол-во альтернатив-
          і                і                       і ных наборов регист-
          і                і                       і ров отображения
          іsave_area_size   dw ?                   і Кол-во байтов в об-
          і                і                       і ласти сохранения
          і                і                       і контекста(также воз-
          і                і                       і вращается функц. 15)
          іDMA_reg_sets     dw ?                   і Кол-во наборов ре-
          і                і                       і гистров, которое мо-
          і                і                       і жет назначаться ка-
          і                і                       і налам ПДП
          і                і                       і 0: работа ПДП по
          і                і                       і стандарту LIM
         DMA_channel_op     dw ?                   і 0: работа ПДП по
          і                і                       і стандарту LIM
          і                і                       і 1: только 1 канал ПДП

                                      - 7-19 -
          іПолучить счетчикіAX:5902h(код функции)  і BX: кол-во неразме-
          інеразмещенных   і                       і щенных исходных
          іисходных страниці                       і страниц
          і                і                       і DX: общее кол-во
          і                і                       і исходных страниц
       27 іРазместить      іAH:5Аh (код функции)   і DX: обработчик
          істандартные/    іAL:00h-разместить      і исходных/стандарт-
          іисходные страни-істандартные страницы   і ных страниц
          іцы              і01h-разместить исход-  і
          і                іные страницы           і
          і                іBX:нет страниц для раз-і
          і                імещения                і
       28 іПолучить альтер-іAX:5В00h(код функции)  і Если BL<>0<--актив-
          інативный набор  і                       і ный альтернативный
          ірегистров отоб- і                       і набор регистров
          іражения         і                       і отображения
          і                і                       і Если BL=0-ES:DI<--
          і                і                       і область сохранения
          і                і                       і контекста регистров
          і                і                       і отображения
          іУстановить аль- іAX:5В01h(код функции)  і Нет
          ітернативный на- іBL:00h                 і
          ібор регистров   іES:DI-->область сохра- і
          іотображения     інения контекста регист-і
          і                іров отображения        і
          і                і<>00h-кол-во альтерна- і
          і                ітивных наборов  регист-і
          і                іров отображения        і
          іПолучить размер іAX:5В02h(код функции)  і DX: кол-во байтов в
          іобласти сохра-  і                       і области сохранения
          інения альтерна- і                       і контекста регистров
          ітивного отобра- і                       і отображения
          іжения           і                       і
          іРазместить аль- іAX:5В03h(код функции)  і BL:0 - нет доступных
          ітернативный на- і                       і альтернативных набо-
          ібор регистров   і                       і ров регистров отоб-
          іотображения     і                       і ражения
          і                і                       і <>0 - кол-во разме-
          і                і                       і щенных альтернатив-
          і                і                       і ных наборов регист-
          і                і                       і ров отображения
          іОсвободить аль- іAX:5В04h(код функции)  і Нет
          ітернативный на- іBL: кол-во альтернатив-і
          ібор регистров   іных наборов регистров  і
          іотображения     іотображения            і
          і                і                       і
          іРазместить на-  іAX:5В05h(код функции)  і BL:0 - наборы регист-
          ібор регистров   і                       і ров ПДП не поддержи-
          іПДП             і                       і ваются
          і                і                       і <>0 - кол-во разме-
          і                і                       і щенных наборов
          і                і                       і регистров ПДП
          іВключить ПДП на іAX:5В06h(код функции)  і Нет
          іальтернативный  іBL:номер набора регист-і
          інабор регистров іров ПДП                і
          іотображения     іDL: номер канала ПДП   і
          іВыключить ПДП наіAX:5В07h(код функции)  і Нет

                                      - 7-20 -
          іальтернативный  іBL:номер набора регист-і
          інабор регистров іров ПДП                і
          іотображения     і                       і
          іОсвободить на-  іAX:5В08h(код функции)  і Нет
          ібор регистров   іBL:номер набора регист-і
          іПДП             іров ПДП                і
       29 іПодготовиться   іAH:5Сh (код функции)   і Нет
          ік загрузке из   і                       і
          іпамяти          і                       і
       30 іВключить набор  іAX:5D00h(код функции)  і BX,CX: ключ доступа
          іфункций OS/E    іBX,CX: ключ доступа    і (возвращаемый толь-
          і                і(требующийся при всех  і ко на первом вызове)
          і                івызовах после первого) і
          іВыключить набор іAX:5D01h(код функции)  і BX,CX: ключ доступа
          іфункций OS/E    іBX,CX: ключ доступа    і (возвращаемый толь-
          і                і(требующийся при всех  і ко на первом вызове)
          і                івызовах после первого) і
          іВозвратить ключ іAX:5D02h(код функции)  і Нет
          ідоступа         іBX,CX: ключ доступа    і
          і                і(возвращаемый первым   і
          і                івызовом включения или  і
          і                івыключения набора функ-і
          і                іций OS/E               і
       ДДДБДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДД


                      Реализации менеджера расширенной памяти

              Исходный менеджер  расширенной  памяти был разработан фирмой
         Intel для обеспечения программного интерфейса с  платами  памяти,
         которые  изготавливались для спецификации расширенной памяти LIM.
         Другие поставщики делали менеджеры  расширенной  памяти,  которые
         подгонялись  к платам расширенной памяти их собственного изготов-
         ления. Однако, реализации менеджера расширенной памяти не ограни-
         чены  по  форме программного драйвера для специального типа платы
         памяти.
              В предыдущем рассмотрении менеджера расширенной памяти отме-
         чалось,  что спецификация расширенной памяти LIM (по крайней мере
         после  версии 3.2) в сущности не зависит от аппаратуры. Несколько
         разработчиков программного обеспечения учли этот факт и продолжа-
         ли создавать менеджеры расширенной памяти, которые не требуют ни-
         какого специального оборудования расширенной памяти. Эти менедже-
         ры расширенной памяти,  на которые обычно ссылаются как на эмуля-
         торы или имитаторы расширенной памяти, моделируют расширенную па-
         мять путем своппинга данных из/на обычной памяти на  диск  или  в
         расширенную память,  присутствующие на многих системах, совмести-
         мых с PC/AT.
              Появление систем,  совместимых с PC/AT и PS/2,  которые  ис-
         пользуют  микропроцессор Intel 80386,  сделало возможным создание
         менеджера расширенной памяти  другого  типа,  который  использует
         усовершенствованную аппаратуру управления памятью, существующую в
         каждом 80386.  Фирма COMPAQ в настоящее время включает  в  каждую
         свою модель Deskpro 386 менеджер расширенной памяти, базирующийся
         на этой возможности, именуемый CEMM.
              В то  время  как каждый вариант менеджера расширенной памяти
         реализует спецификацию расширенной памяти LIM,  каждый тип  также
         характеризуется набором показателей стоимости, производительности

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

           Оборудование и  программное  обеспечение  расширенной памяти

              Вне зависимости от конкретных подробностей конструкции платы
         менеджер расширенной  памяти управляет динамическим переключением
         памяти в и из прямо адресуемую память через набор регистров отоб-
         ражения на плате. Регистры отображения реализуются последователь-
         ностью портов ввода/вывода,  где-то в  пространстве  ввода/вывода
         компьютера. Плата и поддерживающий ее менеджер расширенной памяти
         должны конфигурироваться при установке для приспособления к поль-
         зованию  адресного  пространства ввода/вывода и адресов выше 640К
         адаптерами видео и ввода/вывода, а также ПЗУ BIOS.
                                    Достоинства
              Скорость - можно отображать страницу  расширенной  памяти  в
         страничный  кадр  спецификации расширенной памяти за примерно 100
         микросекунд.
              Широкая доступность - доступны платы для систем с шинной ар-
         хитектурой PC, PC/AT и Microchannel.
                                    Недостатки
              Дорого - платы расширенной памяти большого объема могут сто-
         ить столько, сколько целый компьютер.
              Пространство - требуется одно или более гнезд шины  в  блоке
         компьютера.
              Совместимость - не каждая плата спецификации расширенной па-
         мяти  будет работать в каждой компьютерной системе.  Пользователи
         высокопроизводительных PC/AT должны обращать внимание,  чтобы по-
         купать платы спецификации расширенной памяти,  способные работать
         на скорости шины их системы.

                    Аппаратура и программное обеспечение 80386

              Возможно использовать оборудование управления памятью,  при-
         сутствующее в каждом микропроцессоре 80386,  в сочетании с вирту-
         альным режимом 8086,  имеющимся в 80386. Детали работы управления
         памятью 80386 и режима виртуального 8086 слишком сложны для  объ-
         яснения здесь.  Достаточно сказать, что их хватает для выполнения
         функций отображения, обслуживаемых регистрами отображения страниц
         на сложной плате расширенной памяти.
                                    Достоинства
              Скорость -  отображения страниц можно выполнять за несколько
         микросекунд.
              Недорого - предполагается, что у Вас уже есть машина 80386 с
         большим количеством расширенной памяти.

                                      - 7-22 -
                                    Недостатки
              Дорого -  предполагается,  что  у Вас еще нет машины на базе
         80386 или что у нее нет большого объема расширенной памяти.  Сов-
         местимость  - другое программное обеспечение,  использующее защи-
         щенный режим 80386,  такое как некоторые многозадачные среды  или
         "Расширители DOS",  может конфликтовать с использованием менедже-
         ром расширенной памяти  средств  защищенного  режима,  таких  как
         страничный режим и виртуальный режим 8086. Тестирование менеджера
         расширенной памяти 80386 с фактическим  сочетанием  аппаратуры  и
         программного обеспечения,  с которым он будет применяться,  может
         быть единственным способом, гарантирующим успех.

                          Только программное обеспечение

              На ЦВМ на базе 8086 менеджер  расширенной  памяти  эмулирует
         память  спецификации  расширенной  памяти  путем своппинга данных
         между кадром страниц,  расположенным в обычной памяти,  и  гибким
         или  жестким  диском.  На ЦВМ на базе 80286 с расширенной памятью
         (совместимые с PC/AT и модели 50 и 60 PS/2)  менеджер расширенной
         памяти может эмулировать расширенную память путем своппинга стра-
         ниц спецификации расширенной памяти между расширенной  памятью  и
         кадром  страниц спецификации расширенной памяти в обычной памяти.
                                    Достоинства
              Недорого - менеджер расширенной памяти требует только те ре-
         сурсы, которые обычно в наличии на базовом оборудовании.
                                    Недостатки
              Производительность - перенос данных из обычной памяти в рас-
         ширенную и обратно занимает в десятки и сотни раз больше времени,
         чем при отображении страниц на настоящей плате расширенной  памя-
         ти. Для жесткого диска требуется в сотни и тысячи раз больше вре-
         мени.  Если все, чем Вы обладаете - гибкий диск, потребуется при-
         мерно в миллион раз больше времени.  Возможно удивительно, однако
         существуют определенные приложения памяти спецификации  расширен-
         ной памяти, которые не требуют производительности платы расширен-
         ной памяти,  такие как сохранение текста или графических  экранов
         для последующего повторного вызова или своппинг TSR в и из памяти
         по запросу.
                                   Совместимость
              На настоящей плате (или  эмуляторе  типа  80386)  логическая
         страница  спецификации  расширенной памяти может отображаться бо-
         лее,  чем в одну физическую страницу спецификации расширенной па-
         мяти в один и тот же момент времени, с помощью метода, именуемого
         совмещение имен (который будет рассмотрен подробнее далее в  гла-
         ве).  Поскольку имитаторы копируют страницы памяти, а не "отобра-
         жают" их в различные области адресного  пространства, приложения,
         которые полагаются на совмещение данных,  не могут работать с та-
         ким типом менеджера расширенной памяти.  На практике  большинство
         приложений,  которые  используют спецификацию расширенной памяти,
         не полагаются на совмещение для работы.

                      IBM PS/2 80286 опция увеличенной памяти
              Кратко документированное  средство  опции увеличенной памяти
         IBM 80286 для моделей 50 и 60 PS/2 обеспечивает  набор  регистров
         субадресации, доступный через средство выбора программируемой оп-
         ции архитектуры шины микроканала, которое может быть запрограмми-
         ровано,  для того чтобы работать подобно регистрам отображения на
         настоящей плате расширенной памяти.

                                      - 7-23 -
              Очевидно, эти  регистры  субадресации  были  спроектированы,
         чтобы позволить программам самотестирования при включении питания
         в ПЗУ BIOS переотображать любые отказавшие физические блоки памя-
         ти на старшие адреса памяти (и отображать хорошие блоки на прост-
         ранство, оставшееся после этого в младших адресах), так чтобы ма-
         шина могла работать,  даже после отказа одной или нескольких  БИС
         памяти.
              Также возможно отображение памяти платы увеличенной памяти в
         пространство  ниже  640К  для предоставления отображаемой обычной
         памяти, разрешаемое усовершенствованной спецификацией расширенной
         памяти AQA и спецификацией расширенной памяти LIM 4.0.  К сожале-
         нию,  целый мегабайт памяти,  присутствующей на планарных  платах
         моделей 50 и 60 PS/2, должен быть при этом отключен.
                                    Достоинства
              Недорого - подразумевается, что у Вас уже есть модели 50 или
         60 IBM PS/2 с опцией увеличения расширенной  памяти  IBM.  Другие
         платы  увеличения  памяти для PS/2 могут поддерживать или не под-
         держивать средство субадресации.
              Производительность - менеджер расширенной памяти, написанный
         для пользования этими регистрами,  может точно также работать  на
         сложной плате расширенной памяти.
                                    Недостатки
              Дорого -  подразумевается,  что  у Вас еще нет PS/2 с опцией
         увеличения расширенной памяти IBM.

          Интерфейс прикладной программы спецификации расширенной памяти

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

                 Программирование на языке ассемблера спецификации
                                расширенной памяти

              Программисты на языке  ассемблера,  привыкшие  к  интерфейсу
         системных  вызовов DOS,  найдут,  что менеджер расширенной памяти
         представляет практически идентичную ситуацию:
              1. Поместить  код функции для требующейся функции специфика-
         ции расширенной памяти в регистр AH.
              2. Поместить  другие  аргументы,  необходимые  для выбранной
         функции, и/или структуры данных в память, как определено специфи-
         кацией расширенной памяти LIM.
              3. Передать управление менеджеру  расширенной  памяти  путем
         выдачи программного прерывания 67h.
              4. Менеджер расширенной памяти возвращает  управление  прог-
         рамме,  выдавшей запрос,  перезаписывая код функции, помещенный в
         регистр AH на шаге, кодом состояния для запрошенной операции. Код
         состояния 00h сигнализирует об успешном завершении функции; любое
         другое значение показывает,  что менеджер расширенной памяти  на-
         толкнулся  на  какие-либо  проблемы,  пытаясь выполнить выбранную
         функцию.  Значения кодов ошибок и их смысл  перечислены  далее  в
         этой главе.


                                      - 7-24 -
                                Конфликт прерываний

              Программисты должны знать,  что прерывание 67h не зарезерви-
         ровано "официально" для спецификации расширенной памяти LIM; мно-
         гие другие доступные в продаже программы  также  используют  его.
         Конфликты в использовании этого прерывания часто озадачивают раз-
         работчиков и пользователей программного  обеспечения спецификации
         расширенной памяти,  когда приложение,  которое работает на одной
         машине,  перестает работать на другой с идентичной  конфигурацией
         оборудования.  Возможно  для прерывания 67h,  чтобы оно совместно
         разделялось более, чем одной программой, хотя менеджеры расширен-
         ной  памяти не программируются для выполнения этого.  Даже,  если
         менеджер расширенной памяти способен разделять использование пре-
         рывания 67h с другим программным обеспечением, один из двух доку-
         ментированных методов обнаружения присутствия менеджера расширен-
         ной памяти может не сработать,  если другой обработчик прерывания
         встанет в цепь перед обработчиком прерывания менеджера  расширен-
         ной памяти.
              Поскольку применение прерывания 67h встроено  в спецификацию
         расширенной памяти LIM,  любое приложение, написанное для исполь-
         зования расширенной памяти,  связывается с менеджером расширенной
         памяти  путем  выдачи  этого прерывания. Таким образом, конфликты
         из-за прерывания 67h между менеджером расширенной памяти и други-
         ми  программами  могут  быть  разрешены только,  если программное
         обеспечение,  не относящееся к спецификации  расширенной  памяти,
         может быть реконфигурировано или модифицировано,  чтобы использо-
         вать другой вектор прерывания.

                               Языки высокого уровня

              Как и  в  случае  с  системными вызовами DOS,  не существует
         стандартного интерфейса между языками высокого уровня, такими как
         Си, Паскаль  или  ФОРТРAH и спецификацией расширенной памяти LIM.
         Однако, разработчики, которые желают обращаться к расширенной па-
         мяти из приложений,  написанных на языках высокого уровня, обычно
         имеют несколько жизнеспособных альтернатив.  В  продаже  доступны
         несколько  библиотек  функций спецификации расширенной памяти для
         некоторых различных языков.  Многие популярные  языковые  системы
         обладают подпрограммами или функциями, которые обеспечивают общие
         средства для доступа к регистрам микропроцессора и  выдачи  прог-
         раммных прерываний. Кроме того, языки высокого уровня должны так-
         же обеспечивать какой-либо способ для определения удаленных  ука-
         зателей (регистр сегмента плюс смещение) для  адресации  структур
         данных, передаваемых для менеджера расширенной памяти.
              Программисты, знающие язык ассемблера 808х, знакомые со свя-
         зыванием подпрограмм и соглашениями о передаче параметров их язы-
         ка высокого уровня,  найдут,  что несложно создать набор программ
         сопряжения для спецификации расширенной памяти. Пример такого на-
         бора, написанного на языке Си фирмы Microsoft версия 5.0,  приво-
         дится в конце данной главы вместе с другими программами.

                             Обработка условий ошибок

              Каждый программист, пытающийся написать надежное программное
         обеспечение,  должен ожидать, что придется обрабатывать неизбежно
         появляющиеся ошибки.  Спецификация расширенной памяти LIM 4.0 оп-
         ределяет 36 различных условий ошибок,  которые  может  возвращать

                                      - 7-25 -
         менеджер расширенной памяти, так же как и код, который сигнализи-
         рует об успешном завершении функции. Листинг 7-1 содержит опреде-
         ления для кодов ошибок спецификации расширенной памяти LIM 4.0, а
         в табл. 7-3 перечислены коды состояний ошибок.
              Менеджер расширенной  памяти возвращает детализированный код
         состояния завершения в регистре AH для каждого запроса на  обслу-
         живание при возврате из прерывания.  По отношению к обнаружению и
         сообщению об ошибках интерфейс программирования спецификации рас-
         ширенной памяти более последовательный и менее сложный,  чем DOS.
         Для обнаружения и сообщения условия  ошибки,  последовавшего  при
         вызове DOS,  программист должен проанализировать регистр или флаг
         переноса и затем выдать другой системный  вызов  для  возвращения
         детализированного кода ошибки.
                                                         Таблица 7-3
                  Коды состояний спецификации расширенной памяти
                             Lotus/Intel/Microsoft 4.0
         ДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
         Код і                         Описание
         ДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
         00H іУказанная функция завершилась без ошибок
         80H іОтказ программного обеспечения драйвера менеджера расширенной
             іпамяти
         81H іДрайвер менеджера расширенной памяти  обнаружил  аппаратурный
             іотказ
         82H іДрайвер менеджера расширенной памяти занят (ничего другого не
             іпроизошло)
         83H іHельзя отыскать указанного обработчика
         84H іКод функции неопределен
         85H іВ настоящий момент нет доступных обработчиков
         86H іПроизошла ошибка восстановления контекста отображения
         87H іДля запроса недостаточно общего числа страниц
         88H іДля запроса недостаточно числа неразмещенных страниц
         89H іБыл запрос на нуль логических страниц из функции, совместимой
             іс LIM 3.2
         8AH іЛогическая страница вне диапазона указанного обработчика
         8BH іФизическая страница вне диапазона
         8CH іПереполнение области сохранения контекста регистров отображе-
             іния
         8DH іУ стека контекста регистров отображения  уже  есть  контекст,
             ісвязанный с указанным обработчиком
         8EH іУ стека контекста регистров отображения нет контекста, связан-
             іного с указанным обработчиком
         8FH іБыла запрошена неопределенная подфункция
         90H іТип атрибута не определен
         91H іСистема не поддерживает неразрушаемость (энергонезависимость)
         92H іПри  передвижении  области произошла частичная перезапись ис-
             іточника
         93H іОбласть  расширенной памяти слишком велика для указанного об-
             іработчика
         94H іОбласти обычной и расширенной памяти перекрываются
         95H іСмещение в пределах логической страницы превосходит длину ло-
             ігической страницы
         96H іДлина области превышает предел в 1 Мбайт
         97H іОбласти расширенной памяти источника и приемника имеют один и
             ітот же обработчик и перекрываются
         98H іHеопределенные/неподдерживаемые  типы памяти источника и при-
             іемника

                                      - 7-26 -
         9AH іУказанный  альтернативный  набор регистров отображения не су-
             іществует
         9BH іВсе альтернативные наборы регистров  отображения/ПДП исполь-
             ізуются
         9CH іHе поддерживаются альтернативные наборы регистров отображения
             і/ПДП
         9DH іУказанный  альтернативный  набор регистров отображения/ПДП не
             іопределен, не размещен или является текущим
         9EH іЗакрепленные каналы ПДП не поддерживаются
         9FH іУказанный закрепленный канал ПДП не существует
         A0H іHе  может быть найдено значение обработчика, соответствующее
             іуказанному имени обработчика
         A1H іОбработчик с указанным именем уже существует
         A2H іПопытка перехода к началу 1-Мбайтного  адресного пространства
             іво время передвижения или обмена
         A3H іСодержимое  структуры  данных  пользователя,  переданное  для
             іфункции искажено или бессмысленно
         A4H іОперационная система не допускает обращения к функции
         ДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

                               Листинг 7-1.EMMERR.H
         ------------------------------------------------------------------

         /*

         Продукт:       Диск над;
         Версия:        2.00
         Наименование:  emmerr.h
         Содержание: определения кодов ошибок спецификации расширенной па-
                        мяти LIM 4.0
         Ссылка: спецификация расширенной памяти LIM версия 4.0 стр.А5-А10
         */

        #defineFRSTEMERR  0x80  /*номер первой ошибки менеджера расширенной
                                                           памяти*/
        #defineLASTEMERR  0xA4  /*номер последней ошибки менеджера расши-
                                                          ренной памяти*/

        #defineFUNCCOC    0x00  /*указанн. функция завершилась без ошибок*/
        #defineEMDRVSWF   0x80  /*отказ  программного  обеспечения драйвера
                                             менеджера расширенной памяти*/
        #defineEMDRVHWF   0x81  /*драйвер менеджера расширенной памяти
                                             обнаружил аппаратурный отказ*/
        #defineEMDRVBSY   0x82  /*драйвер менеджера расширенной памяти
                                      занят (ничего другого не произошло)*/
        #defineHANDLNFD   0x83  /*нельзя отыскать указанного обработчика */
        #defineFUNCCUND   0x84  /*код функции неопределен */
        #defineHANDLINS   0x85  /*в настоящий момент нет доступных обра-
                                                                ботчиков*/
        #defineMAPCXPRO   0x86  /*произошла ошибка восстановления контекста
                                                              отображения*/
        #defineTOTPGINS   0x87  /*для запроса   недостаточно   общего
                                                           числа  страниц*/
        #defineUNAPGINS   0x88  /*для  запроса недостаточно числа
                                                    неразмещенных страниц*/
        #defineLPAGE2SM   0x89  /*был запрос на нуль логических страниц из
                                          функции,  совместимой с LIM 3.2*/

                                      - 7-27 -
        #defineLPAGERNG   0x8A  /*логическая страница вне диапазона ука-
                                                      занного обработчика*/
        #definePPAGE2BG   0x8B  /*физическая страница вне диапазона*/
        #defineMRCSAFUL   0x8C  /*переполнение области сохранения контекста
                                                   регистров  отображения*/
        #defineMRCSTDUP   0x8D  /*у  стека  контекста регистров отображения
                                уже есть контекст, связанный  с  указанным
                                                             обработчиком*/
        #defineMRCSTNFD   0x8E  /*у стека контекста регистров отображения
                                нет контекста, связанного с указанным об-
                                                               работчиком*/
        #defineSFUNCUND   0x8F  /*была запрошена неопределенн. подфункция*/
        #defineATTRBUND   0x90  /*тип атрибута не определен*/
        #defineNVSTGUNS   0x91  /*система  не поддерживает неразрушаемость
                                                    (энергонезависимость)*/
        #defineMREGNOVW   0x92  /*при передвижении области произошла
                                           частичная перезапись источника*/
        #defineMREGN2SM   0x93  /*область расширенной памяти слишком велика
                                  для указанного обработчика*/
        #defineMREGNOVL   0x94  /*области обычной и расширенной памяти пе-
                                                           рекрываются*/
        #defineLPGOF2BG   0x95  /*смещение  в пределах логической страницы
                                 превосходит длину логической страницы*/
        #defineMREGN2BG   0x96  /*длина области превышает предел в 1Мбайт*/
        #defineMREGNDUP   0x97  /*области расширенной памяти источника и
                                приемника имеют один и тот же обработчик и
                                                            перекрываются*/
        #defineMREGNUND   0x98  /*неопределенные/неподдерживаемые  типы
                                             памяти источника и приемника*/
        #defineAMRSNFD    0x9A  /*указанный альтернативный набор регистров
                                                отображения не существует*/
        #defineAMDRSINS   0x9B  /*все альтернативные наборы регистров отоб-
                                                 ражения/ПДП используются*/
        #defineAMDRSUNS   0x9C  /*не поддерживаются альтернативные наборы
                                                регистров отображения/ПДП*/
        #defineAMDRSUND   0x9D  /*указанный альтернативный набор регистров
                                отображения/ПДП не определен,  не размещен
                                                     или является текущим*/
        #defineDDMACUNS   0x9E  /*закрепленные каналы ПДП не поддерж-ся*/
        #defineDDMACNFD   0x9F  /*указанный закрепленный канал ПДП не су-
                                                                 ществует*/
        #defineHNDVLNFD   0xA0  /*не может быть найдено значение обработчи-
                                ка, соответствующее  указанному  имени об-
                                                                работчика*/
        #defineHNDNMDUP   0xA1  /*обработчик с указанным именем уже сущест-
                                                                     вует*/
        #defineMREGNWRP   0xA2  /*попытка перехода к началу 1-Мбайтного
                                адресного пространства во время передвиже-
                                                           ния или обмена*/
        #defineUSRDSFMT   0xA3  /*содержимое структуры данных пользователя,
                                переданное для функции  искажено  или бес-
                                                                смысленно*/
        #defineOPSYSACC   0xA4  /*операционная система не допускает обраще-
                                                            ния к функции*/
         -------------------------------------------------------------------



                                      - 7-28 -
         Поскольку  проверка  на  наличие  ошибок,  обнаруженных  монитором
         расширенной памяти  -  просто  вопрос тестирования регистра AH на
         нуль после каждого вызова,  всегда  оказывается  разочаровывающим
         наталкивание  на популярное коммерческое программное обеспечение,
         которое не удосуживается проделать это.  Избавьте себя  (и  Ваших
         пользователей) от забот отлавливания загадочных зависаний и отка-
         зов посредством проверки кода ошибки после каждого вызова  менед-
         жера расширенной памяти.
              Реакция программы на условие ошибки, возвращенное менеджером
         расширенной памяти,  зависит от природы ошибки и  от  возможности
         программы приспособиться к условиям, на которые спецификация рас-
         ширенной памяти LIM ссылается как на "невосстанавливаемые".   На-
         пример, программа может сделать очень немногое, получив от менед-
         жера расширенной памяти индикацию "Отказ  аппаратуры  расширенной
         памяти",  помимо  сообщения о проблеме пользователю и воздержания
         от дополнительного использования обслуживанием  менеджера  расши-
         ренной памяти.
              И, наоборот, адаптирующаяся программа могла бы быть способна
         восстанавливаться  из условий таких,  как "Недостаточно доступных
         страниц спецификации расширенной памяти", возможно, путем исполь-
         зования  дискового  файла в качестве временной области сохранения
         для данных, которые не могут поместиться в расширенной памяти.
              Другие условия такие,  как "Физическая страница вне диапазо-
         на", обычно показывают, что имеет место ошибка проектирования или
         программирования  в  приложении  спецификации расширенной памяти.
         Реализуйте Ваши программы обработки ошибок спецификации расширен-
         ной памяти так,  чтобы они сообщали ячейку в Вашей программе, где
         появилось условие ошибки,  предпочтительно таким образом, который
         связан с исходным кодом.

            Написание программ, которые пользуются расширенной памятью

              Все программы, которые пользуются расширенной памятью, долж-
         ны соблюдать определенный протокол.  Каждая программа должна  вы-
         полнять нижеследующие шаги в том порядке, как они перечислены:
              1. Обнаружить наличие менеджера расширенной памяти.
              2. Определить, есть ли в наличии достаточное для Вашего при-
         ложения количество страниц расширенной памяти.
              3. Получить адрес начала кадра страниц.
              4. Разместить страницы расширенной памяти.
              5. Отобразить страницы расширенной памяти в кадр страниц.
              6. Считывать,  записывать или выполнять данные в расширенной
         памяти.
              7. Возвратить страницы расширенной памяти  менеджеру  расши-
         ренной памяти перед завершением Вашего приложения.

                  Общие руководящие указания по программированию

              Характеристики расширенной памяти  и  менеджера  расширенной
         памяти  накладывают относительно небольшое количество ограничений
         на программы, которые их используют. Следующие руководящие указа-
         ние относятся ко всем программам,  которые пользуются расширенной
         памятью:

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

                                      - 7-29 -
         ной памяти отображается более,  чем в  одну  физическую  страницу
         кадра страниц.  В реализациях менеджера расширенной памяти, кото-
         рые используют аппаратуру  отображения  страниц,  эффект  данного
         способа  заключается в том,  что 16-Кбайтная страница расширенной
         памяти будет появляться более,  чем в одном 16-Кбайтном блоке ад-
         ресного  пространства  процессора.  Менеджеры расширенной памяти,
         написанные для истинных плат спецификации расширенной памяти, ап-
         паратурного страничного механизма 80386 и опции расширенной памя-
         ти 80286 IBM PS/2 - все могут поддерживать данный способ. Однако,
         чисто программные эмуляторы спецификации расширенной памяти,  ко-
         торые имитируют отображение страниц путем копирования блоков дан-
         ных в память, не могут выполнять совмещение данных.
              Ваша программа может выполнить следующую проверку  для того,
         чтобы определить поддерживает ли менеджер расширенной памяти сов-
         мещение данных:
              1. Отобразить  одну  логическую страницу,  по крайней мере в
         две физические.
              2. Записать данные в одну из физических страниц.
              3. Если данные,  записанные в физическую страницу на шаге 2,
         также  появляются  в других физических страницах,  в которые была
         отображена логическая страница, тогда реализация менеджера расши-
         ренной памяти поддерживает совмещение данных.
              На рис.  7-2  иллюстрируется  совмещение  данных.  Поскольку
         единственная  логическая  страница отображается в первую и третью
         физические страницы в пределах кадра страниц,  к элементу данных,
         находящемуся по смещению 2132Н в логической странице, можно обра-
         титься по физическим адресам CC00:2132 и D400:2132.

              * Приложения должны возвращать все размещенные  страницы  ме-
         неджеру расширенной  памяти  перед  завершением  программы.  Ваша
         программа должна возвратить каждый обработчик расширенной памяти,
         размещенный Вашей программой,  перед ее нормальным или ненормаль-
         ным завершением.
           Начальный сегмент     N физической
           физической страницы     страницы
                        CC00H    ЪДДДДДДДїДД      Элемент данных по
                                 і       і   \    смещению 2132Н в
                                 і   0   і     \  логической странице
                        D000H    ГДДДДДДДґД      \ЪДДДДДДДДДДДДДДДДДї
                                 і       і \    / і       ЪДДДДДДї  і
                                 і   1   і   \/   і       і123.45і  і
                        D400H    ГДДДДДДДґДД/  \  і       АДДДДДДЩ  і
                                 і       і      /\АДДДДДДДДДДДДДДДДДЩ
                                 і   2   і    /
                        D800H    ГДДДДДДДґДД/
                                 і       і
                                 і   3   і    Чтение/Запись: физические
                                 АДДДДДДДЩ    адреса CC00:2132 или
                                              D400:2132 будут ссылаться
                                              на один и тот же элемент
                                              данных в расширенной памяти

             Рис. 7-2. Совмещение данных  -  одна логическая страница,
                     отображенная более чем в одну физическую страницу

              Отказ от возврата всех страниц менеджеру  расширенной памяти
         может  вызвать  отказы  на последующие запросы расширенной памяти

                                      - 7-30 -
         вследствие нехватки страниц или обработчиков. Убедитесь, что коды
         выхода  по ошибке или аварийному завершению Вашей программы осво-
         бождают расширенную память также,  как и код нормального заверше-
         ния.

              * Отображайте  данные только в обычную память,  которую Ваша
         программа разместила из DOS.  Функция передвижения/обмена  облас-
         тей, введенная в спецификации расширенной памяти LIM 4.0, сделала
         возможным своппинг данных в или из адресного пространства, управ-
         ляемого DOS.  Перед своппингом любых данных в адресное пространс-
         тво DOS Ваша программа должна разместить их из DOS,  так как  ме-
         неджер расширенной памяти не управляет обычной памятью.  Отказ от
         соблюдения этого правила, вероятно, приведет к искажению данных и
         краху системы.

              * Любая структура данных,  адрес которой передается в вызове
         функции менеджера расширенной  памяти,  не  должна  находиться  в
         отображаемой памяти. За исключением функций "Изменить отображение
         страницы и перейти" и "Сменить отображение страницы  и  вызвать",
         которые  были специально разработаны для поддержки выполнения ко-
         довых объектов в расширенной памяти, структуры данных, адреса ко-
         торых передаются менеджеру расширенной памяти,  должны находиться
         в памяти, которая не может перестать отображаться. Например, Ваша
         программа  не может запоминать области сохранения контекста отоб-
         ражения в расширенной памяти.

              * Не размещайте стек программы в расширенной памяти. Если Вы
         используете  расширенную  память  для хранения и выполнения кода,
         его стек должен размещаться в  обычной  (неотображаемой)  памяти.
         Если  программа  обслуживания  прерывания  получает управление от
         программы,  стек которой находится в расширенной  памяти,  чтение
         или запись данных в стек,  который перестал отображаться, привело
         бы к отказу.

           Применение расширенной  памяти  в  нерезидентных  программах

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

                 Обнаружение наличия менеджера расширенной памяти

              В спецификации расширенной памяти  LIM  документируются  два
         способа, которые могут применяться для обнаружения наличия расши-
         ренной памяти: метод открытого обработчика и метод получения век-
         тора  прерывания.  Для стандартных программ DOS,  какой метод  Вы
         выберете - вопрос предпочтения. Идея метода открытого обработчика
         представлена  здесь;  метод  получения  вектора  прерывания будет
         представлен в разделе,  описывающем использование расширенной па-
         мяти с резидентными программами.

                            Метод открытого обработчика
              Для обнаружения  наличия  менеджера расширенной памяти с по-

                                      - 7-31 -
         мощью метода открытого обработчика используется ряд системных вы-
         зовов  файлов DOS,  для выявления присутствия драйвера устройства
         менеджера расширенной памяти и, в случае его присутствия, для оп-
         ределения его способности обслуживать запросы для прерывания 67h.
         Метод действует следующим образом:
              1. Выполняется вызов открытого обработчика DOS (функция DOS-
         3Dh), определяющий доступ только по чтению (режим 0) с именем пу-
         ти  доступа  EMMXXXX0.  Это - имя драйвера символьного устройства
         менеджера расширенной памяти,  который был  установлен  во  время
         первичной загрузки, если для драйвера устройства менеджера расши-
         ренной памяти в файле CONFIG.SYS было указано DEVICE=элемент.
              2. Если  вызов  открытого обработчика не выполняется с кодом
         возврата "не найдено имя файла или пути доступа",  то  Вы  можете
         полагать, что расширенная память отсутствует. Вызов открытого об-
         работчика может также не выполниться, если все обработчики файлов
         DOS  используются перед тестом наличия.  Для предотвращения этого
         Ваша программа должна выполнять тест наличия  расширенной  памяти
         до открытия любого другого файла.
              3. Если вызов открытого обработчика завершается успешно, это
         показывает, что существует файл или устройство с именем менеджера
         расширенной памяти.  Для того, чтобы установить, относится ли об-
         работчик, возвращенный на шаге 1, к устройству или файлу, выдайте
         вызов "Управление ввода/вывода для устройств" (IOCTL)  -  функция
         DOS  44h - с подфункцией "Получить информацию об устройстве" (ре-
         гистр AL=00h) для обработчика файла, возвращенного на шаге 1.
              4. Если обработчик принадлежит устройству, бит 7 регистра DL
         будет равен 1,  что показывает на присутствие менеджера расширен-
         ной памяти.  Если бит 7 равен 0,  обработчик связан с файлом, так
         что Вы можете полагать, что расширенная память отсутствует.
              5. Если  обработчик  ссылается на устройство,  выдайте вызов
         IOCTL  с  подфункцией  "Получить  выходное  состояние"   (регистр
         AL=07h) для данного обработчика, для того чтобы определить готов-
         ность менеджера расширенной памяти обрабатывать запросы на обслу-
         живание расширенной памяти.
              6. Если  менеджер расширенной памяти готов обрабатывать  за-
         просы обслуживания расширенной памяти, по вызову IOCTL возвратит-
         ся значение 0FFh в регистре AL.  В противном случае менеджер рас-
         ширенной памяти отсутствует или неспособен  обрабатывать  запросы
         на обслуживание расширенной памяти.
              7. Если начальный открытый обработчик DOS достиг цели,  зак-
         ройте его с помощью вызова закрытия обработчика (функция DOS 3h).
         Данный  обработчик  более не нужен,  поскольку дальнейшее общение
         между менеджером расширенной памяти и Вашим приложением  происхо-
         дит  через интерфейс прерывания 67h и не использует файловую сис-
         тему DOS.

                 Проверка версии спецификации расширенной памяти,
                   поддерживаемой менеджером расширенной памяти

              Если менеджер расширенной памяти существует и готов обслужи-
         вать запросы, выдайте функцию 7 "Получить версию", для того чтобы
         удостовериться,  что версия менеджера расширенной памяти, с кото-
         рым Ваша программа сообщается,  поддерживает версию  спецификации
         расширенной  памяти,  которая  требуется Вашей программе.  Данная
         функция возвращает число в двоично-десятичном коде из двух цифр в
         регистре AL.  Старшие четыре бита числа показывают основной номер
         версии.  Младшие четыре бита или дробная часть этого числа  могут

                                      - 7-32 -
         использоваться  поставщиками  для  обозначения усовершенствований
         или коррекции ошибок для их менеджеров расширенной памяти. Следо-
         вательно,  Вашей  программе для проверки версии не следует связы-
         ваться с обеими цифрами.  Намного лучшая стратегия заключается  в
         выполнении сравнения на "больше или равно".
              В случае MS-DOS 4.0 пользователь мог бы устанавливать  драй-
         веры  третьей  стороны  для  компенсации недостатков в драйверах,
         обеспечиваемых  более ранними выпусками операционной системы, по-
         этому недостаточно  выполнить  проверку на MS-DOS 4.0 и полагать,
         что драйверы  спецификации  расширенной  памяти  присутствуют.  В
         действительности, драйверы MS-DOS 4.0 могут быть установлены, но,
         однако,  не управлять оборудованием.  В качестве простого  теста,
         вызовите функцию 1, "Получить состояние", поместив значение 40h в
         регистр AH и вызвав прерывание 67h; если аппаратура и программное
         обеспечение работают совместно, функция возвратит значение нуль в
         AH;  если нет,  Вы получите ненулевое значение.  Любое  ненулевое
         значение указывает на отказ, но значения 80h или 81h указывают на
         отказ оборудования, типичный при несоответствии драйверов. В этом
         случае  отобразите пользователю сообщение о том,  что драйверы не
         могут работать правильно. Если Ваше приложение тестирует работос-
         пособность для спецификации расширенной памяти MS-DOS 4.0, помни-
         те,  что ранние версии MS-DOS 4.0 не  поддерживают  функцию  19h,
         "Получить/установить   атрибут обработчика",  спецификации расши-
         ренной памяти LIM.

                 Определение доступного объема расширенной памяти

              До того как Ваша программа сможет  пользоваться  расширенной
         памятью,  она  должна явно разместить ее из менеджера расширенной
         памяти. Первый шаг в данном направлении заключается в определении
         наличия достаточного количества страниц для удовлетворения требо-
         ваний Вашей программы путем выдачи функции 3 спецификации  расши-
         ренной  памяти  "Получить  счетчик  числа неразмещенных страниц".
         Данная функция возвращает общее количество  16-Кбайтных  страниц,
         управляемых менеджером расширенной памяти, а также число страниц,
         доступных Вашей программе.

                           Размещение расширенной памяти

              В зависимости от динамического поведения Вашей  программы Вы
         можете  выбирать,  разместить ли всю расширенную память,  которая
         будет необходима единовременно,  или делать отдельные запросы  на
         размещение, когда требования времени выполнения программы меняют-
         ся.  Первый вариант более всего подходит, если количество страниц
         в  течение  времени выполнения варьируется незначительно.  Второй
         вариант более уместен, если ожидается, что требования расширенной
         памяти  программы  будут расти и убывать существенно во время вы-
         полнения  программы.  Этот вариант также более целесообразен, по-
         скольку остается  больше  расширенной  памяти,  доступной  другим
         программам, которые могут выполняться параллельно. Например, одна
         популярная программа электронных таблиц восстанавливает доступный
         объем расширенной памяти и размещает 1/8 этого объема каждый раз,
         когда требуется дополнительная память. Конечно, существует другой
         популярный пакет электронных таблиц,  который размещает целый не-
         размещенный пул расширенной памяти,  когда начинает  выполняться,
         не оставляя ничего другим программам,  которые впоследствии могут
         вызываться самой этой программой.
              При спецификации расширенной памяти LIM  3.2  обеспечивалась
         только  одна  функция размещения расширенной памяти:  "Разместить
         страницы", функция 4 спецификации расширенной памяти. Каждый зап-
         рос на размещение возвращает отдельный обработчик, который следу-
         ет использовать для ссылки на страницы, относящиеся к этому конк-
         ретному  размещению.   Количество   страниц,   связанное  с  этим
         обработчиком,  фиксировано с того момента,  когда  страницы  были
         размещены,  до того, когда они возвращаются менеджеру расширенной
         памяти.  Это ограничение препятствует динамическому размещению  и
         освобождению  расширенной памяти,  так как невозможно вернуть ме-
         неджеру расширенной памяти только некоторые из страниц, принадле-
         жащие конкретному обработчику.
              Данное ограничение было снято в спецификации расширенной па-
         мяти LIM 4.0 с помощью функции "Переразместить страницы", функция
         18 спецификации расширенной памяти. Она позволяет увеличивать или
         уменьшать количество страниц,  связанных с обработчиком,  в любой
         момент после размещения.  Новая функция не снимает всех  проблем,
         связанных  с динамическим управлением структурами данных в памяти
         спецификации расширенной памяти,  поскольку  логические  страницы
         могут добавляться или исключаться только с конца.  Например, если
         бы Вы хотели освободить третью логическую  страницу  обработчика,
         для  которого были размещены шесть логических страниц,  Вам приш-
         лось бы освобождать также логические страницы с четвертой по шес-
         тую. Данная функция также позволяет возвратить все страницы, свя-
         занные с обработчиком,  менеджеру расширенной памяти без возврата
         самого обработчика путем указания нового счетчика страниц, равно-
         го нулю.
              Для того,  чтобы завершить обсуждение размещения расширенной
         памяти,  рассмотрим идею исходных страниц, введенную в специфика-
         ции  расширенной  памяти LIM 4.0.  Исходные страницы - логические
         страницы,  которые некоторым подмножеством  стандартного  размера
         страниц  спецификации  расширенной памяти - 16Кбайт - были ведены
         для обеспечения дополнительной гибкости оборудования и  программ-
         ного  обеспечения  расширенной  памяти,  способного  поддерживать
         меньшие размеры страниц.  Например,  менеджер расширенной памяти,
         базирующийся на аппаратуре Intel 80386,  мог бы поддерживать раз-
         мер исходных страниц, равный 4Кбайт, обеспечивая надлежащим обра-
         зом  написанное  программное  обеспечение  способностью управлять
         расширенной памятью более  эффективно,  чем было бы возможно  при
         размере страниц в 16Кбайт.  Менеджеры расширенной памяти специфи-
         кации расширенной памяти 4.0, написанные для поддержки существую-
         щих  плат спецификации расширенной памяти,  поддерживают идею ис-
         ходных страниц тривиальным образом  -  размер  исходной  страницы
         идентичен размеру стандартной страницы.
              Исходные страницы размещаются с помощью функции 27, подфунк-
         ции 1,  "Разместить исходные страницы",  спецификации расширенной
         памяти.  У функции 27 спецификации расширенной памяти также  есть
         подфункция 0,  "Разместить стандартные страницы", которая обеспе-
         чивает идентичную услугу для функции "Разместить страницы" специ-
         фикации расширенной памяти LIM 3.2 с одним расширением:  она поз-
         воляет разместить нуль страниц для обработчика. Данное расширение
         также имеет место для подфункции 1.

                           Адресация расширенной памяти

              Адреса обычной  памяти  на процессорах Intel 80х86 (в режиме
         реальных адресов) определяются парой 16-битовых компонент: значе-

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

                          Управление логическими адресами

              Спецификация расширенной памяти LIM определяет обработчик как
         16-битовую величину (даже  когда поддерживается максимум 255 обра-
         ботчиков)  и номер логической страницы как 14-битовое число (логи-
         ческие страницы нумеруются от 0 до 2047).  Субадресация  объектов
         внутри  логической страницы не имеет отношения к менеджеру расши-
         ренной памяти,  но является главной заботой разработчика приложе-
         ния расширенной памяти. В особенности, данные какого типа следует
         хранить в расширенной памяти и как ими следует управлять?
              Общий метод  сохранения  следа элемента данных в расширенной
         памяти без обращения к побитным трюкам требует от Вашей программы
         обеспечивать три  16-битовые  переменные на элемент - обработчик,
         номер страниц и байтовое смещение внутри страницы.  Эти накладные
         расходы  делают  применение расширенной памяти наиболее пригодным
         для запоминания структур данных,  которые относительно велики  по
         сравнению с указателями,  применяемыми для обращения к ним.  Мало
         смысла сохранять 4-байтовый элемент данных в  расширенной памяти,
         если  при этом требуется 6-байтовый указатель для доступа к нему.
              Еще одним фактором для  рассмотрения  является  динамическое
         поведение  структур  данных,  которые  Вы намереваетесь хранить в
         расширенной памяти.  Поддержание связного списка,  состоящего  из
         элементов переменной длины могло бы быть интересным предложением,
         если он размещается в расширенной памяти.  Довольно сложная схема
         управления  памятью  потребовалась бы для того,  чтобы эффективно
         уплотнять свободное  пространство  и  обрабатывать  переполнение,
         когда размер списка превысит размер логической страницы.
              В действительности, эффективные способы управления расширен-
         ной  памятью  совершенно аналогичны методам поддержания файлового
         буфера и индексации, применяемым системами управления базами дан-
         ных.  Существующая проблема является проблемой управления элемен-
         тами данных переменного размера (записи,  массивы и т.д.) в огра-
         ниченном  множестве  буферов  фиксированного  размера (16-Кбайт в
         случае спецификации расширенной памяти). Неудивительно, что неко-
         торые программы баз данных для персональных компьютеров,  совмес-
         тимых с IBM PC,  пользуются преимуществами данного сходства путем
         хранения  индексов файлов или,  даже целых файлов,  в расширенной
         памяти, когда она имеется в наличии.
              Потребовались бы  значительные усилия для использования рас-
         ширенной памяти в качестве  средства  управления  памятью  общего
         назначения в Ваших программах.  Однако,  Вы можете эффективно ис-
         пользовать расширенную память во многих обычных задачах  сохране-

                                      - 7-35 -
         ния данных при простых специальных подходах.  Примеры приложений,
         перечисленные в конце данной главы показывают, какие относительно
         несложные  методы  управления  расширенной памятью достаточны для
         обработки задач управления динамической памятью. Последующие раз-
         делы  в данной главе посвящены детальному рассмотрению нескольких
         функций,  добавленных в спецификации расширенной памяти LIM  4.0,
         которые значительно сокращают усилия по программированию, необхо-
         димые для работы с кодами и данными в расширенной памяти.

                          Управление физическими адресами

              В спецификации  расширенной  памяти LIM 3.2 физический адрес
         элемента данных в логической странице,  которая в  данный  момент
         отображается  в  физическую страницу,  вычисляется по отношению к
         базе 64-Кбайтного кадра страниц.  Адрес сегмента для этого  кадра
         страниц получается посредством функции 2  "Получить адрес сегмен-
         та кадра страниц",  спецификации расширенной памяти. Деление раз-
         мера кадра страниц на размер стандартной логической страницы дает
         четыре физических страницы,  пронумерованных от 0 до  3,  которые
         могут  указываться в вызовах функции спецификации расширенной па-
         мяти. Все четыре страницы являются смежными в памяти - адрес каж-
         дой следующей на 16Кбайт выше, чем адрес предшествующей.
              Спецификация расширенной памяти LIM 4.0,  включающая в  себя
         идею многих, возможно несмежных, кадров страниц усовершенствован-
         ной спецификации расширенной памяти AQA,  делает вычисление физи-
         ческого  адреса или более простым или более сложным в зависимости
         от Вашей точки зрения.  В дополнение к функции 2 спецификации LIM
         4.0  менеджер  расширенной  памяти  может  теперь обеспечить Вашу
         программу таблицей номеров физических страниц и  адресов  сегмен-
         тов, соответствующих каждой физической странице.
              Функция 25, подфункция 1, "Получить отсчет элементов адресов
         физической страницы",  спецификации расширенной памяти возвращает
         количество отображаемых физических страниц, поддерживаемых менед-
         жером расширенной памяти.  Используйте эту подфункцию для опреде-
         ления размера массива, адрес которого передается функции 25, под-
         функции 0,  "Получить массив  отображаемых  физических  адресов",
         спецификации  расширенной памяти,  которая заполнит данный массив
         адресами сегментов в нарастающем порядке и номерами соответствую-
         щих страниц.
              В то  время как в спецификации расширенной памяти LIM 4.0 не
         требуется от менеджера расширенной памяти обеспечивать более  че-
         тырех стандартных физических страниц, определенных в спецификации
         3.2,  менеджер  расширенной  памяти  может  обеспечивать  до   36
         16-Кбайтных физических страниц. До 12 страниц может размещаться в
         пространстве между 768К и 960К (шестнадцатиричные адреса от С0000
         до  F0000)  и  до  24 страниц может размещаться между 256К и 640К
         (шестнадцатиричные адреса от 40000 до A0000).
              Доступ к кадру страниц над 640К возможен для любого приложе-
         ния спецификации расширенной памяти. Доступ к отображаемой памяти
         ниже 640К,  на которую спецификация ссылается как на отображаемую
         обычную память,  предназначен для разработчиков расширений опера-
         ционной системы, таких как Windows 2.0 фирмы Microsoft.
              Количество физических страниц в кадре страниц над 640К может
         изменяться  в  зависимости от раскладки пространства ПЗУ на конк-
         ретной машине.  Видеоадаптеры различных типов могут занимать  су-
         щественную  долю  пространства  ПЗУ для своих дисплейных буферов.
         Многие типы адаптеров ввода-вывода,  включая сетевые платы и дис-

                                      - 7-36 -
         ковые контроллеры,  содержат расширения ПЗУ BIOS, которые появля-
         ются в адресном пространстве между C0000 и F0000.
              Программные эмуляторы  должны  обычно размещать кадр страниц
         спецификации расширенной памяти вне адресного  пространства  DOS,
         ниже  640К,  потому что у большинства компьютеров,  совместимых с
         IBM PC, нет ОЗУ между 640К и 960К. Некоторые программные эмулято-
         ры спецификации расширенной памяти могут пользоваться преимущест-
         вом возможности некоторых плат расширенной памяти  отображать 64К
         или более ОЗУ в пространство над 640К.
              Опасно делать какие-либо упрощающие предположения о  положе-
         нии и выравнивании кадра страниц. Некоторые существующие приложе-
         ния спецификации расширенной памяти делают такие допущения, пола-
         гаясь   на  выравнивание  кадра  страниц,  обычно  обеспечиваемое
         оборудованием спецификации расширенной  памяти,  для  того  чтобы
         сохранить  пространство  памяти для указателей расширенной памяти
         или для упрощения вычисления адресов в расширенной памяти.  Одна-
         ко,  кадр страниц, обеспечиваемый чисто программными реализациями
         менеджера расширенной памяти, может не обладать таким же выравни-
         ванием,  как  кадр страниц платы спецификации расширенной памяти.
         Для того чтобы позволить Вашим приложениям использоваться в  сис-
         темах  с чисто программными менеджерами расширенной памяти, здесь
         приводятся некоторые упрощающие допущения,  которые  Вам  следует
         избегать при программировании:
              1. Поскольку  аппаратура  спецификации  расширенной   памяти
         обеспечивает кадры страниц,  выровненные на 16-Кбайтных границах,
         только старший байт адреса сегмента имеет значение при формирова-
         нии физического адреса. В результате некоторые программы не запо-
         минают младший байт сегмента кадра страниц, полагая, что он будет
         равен нулю. В спецификации расширенной памяти LIM не указывается,
         что физические страницы должны выравниваться  по  любой  границе,
         более высокой, чем граница параграфа.
              2. Некоторые приложения спецификации расширенной памяти  вы-
         полняют вычисление адреса расширенной памяти,  полагая,  что кадр
         страниц находится над 640К или,  что его адрес в  памяти  больше,
         чем адрес самого приложения.  Кадр страниц,  обеспечиваемый прог-
         раммным менеджером расширенной памяти может не  подчиняться этому
         допущению.

                        Чтение и запись расширенной памяти

              В то  время как спецификация расширенной памяти LIM 4.0 поз-
         воляет менеджеру расширенной памяти управлять до 32 Мбайтами дан-
         ных, объем, доступный Вашей программе в каждый данный момент вре-
         мени ограничен числом физических страниц,  присутствующих в кадре
         страниц.
              Перед чтением или записью данных из расширенной памяти долж-
         на  быть сделана доступной для программы логическая страница,  на
         которой эти данные размещены,  путем отображения логической стра-
         ницы в физическую страницу в кадре страниц. Отображение страниц -
         в действительности  сердцевина  управления  памятью  спецификации
         расширенной памяти; для большинства приложений, которые пользуют-
         ся расширенной памятью, это - наиболее часто используемая функция
         менеджера расширенной памяти.
              В спецификации расширенной памяти LIM 3.2  для  данной  цели
         обеспечена  функция  5, "Отобразить/перестать отображать страницы
         обработчика". Путем поддержания  обработчика,  номера  логической
         страницы  и  номера  физической страницы одна логическая страница

                                      - 7-37 -
         отображается в одну физическую. Указание -1 или 0FFFFH в качестве
         номера логической страницы делает любую логическую страницу,  ко-
         торая отображается в указанную физическую, недоступной программе.
         Конечно,  содержимое страницы, которая перестала отображаться та-
         ким образом,  не  изменяется и может снова сделать доступным пос-
         ледующие отображением этой логической страницы в физическую.
              Функция 17, "Отобразить/перестать отображать страницы многих
         обработчиков",  спецификации  расширенной памяти LIM 4.0 добавила
         более краткие и гибкие средства отображения страниц.  В одном об-
         ращении  данная функция может отобразить или перестать отображать
         логические страницы в такое число страниц,  которое  поддерживает
         менеджер расширенной памяти.  Программы, которые часто отображают
         много страниц за раз, могут достигать явно более высокой произво-
         дительности  из-за  сокращения  фиксированных накладных расходов,
         связанных с каждым вызовом менеджера расширенной памяти.
              Как и в случае функции 5, указание номера логической страни-
         цы,  равного -1 (0FFFFH) заставляет  любую  логическую  страницу,
         отображенную  в указанную физическую страницу, перестать отобра-
         жаться.

                      Два способа задания физических страниц

              Начальное рассмотрение физических страниц в связи со  специ-
         фикацией  расширенной  памяти LIM 3.2 установило,  что физические
         страницы определяются порядковым номером (от 0 до 3 в  специфика-
         ции расширенной памяти LIM 3.2). В спецификации расширенной памя-
         ти LIM 4.0 обеспечивается дополнительный способ задания  физичес-
         ких  страниц:  фактическим  адресом  сегмента  начала  физической
         страницы.  Например, если адрес кадра страниц, возвращенный функ-
         цией "Получить адрес кадра страниц",  был равен CC00h, третья фи-
         зическая страница в пределах кадра страниц могла  бы определяться
         своим порядковым номером,  2, или адресом сегмента, D400h. Данный
         адрес сегмента был вычислен путем прибавления трижды размера  фи-
         зической страницы (в параграфах) к базовому адресу кадра страниц.
              Любые функции спецификации расширенной памяти LIM 4.0, беру-
         щие номера физических страниц в  качестве  параметров,  позволяют
         указание  физических  страниц  порядковыми  номерами или адресами
         сегментов. Вы можете выбирать наиболее удобный для Вашей програм-
         мы  метод путем задания кода подфункции в регистре AL для функций
         спецификации расширенной памяти 4.0, которые принимают номера фи-
         зических страниц. Код подфункции 00h показывает, что значения фи-
         зических страниц определены порядковыми номерами физических стра-
         ниц,  в то время как код подфункции 01h показывает,  что значения
         физических страниц заданы соответствующими адресами сегментов.
              Как было  описано  в  предшествующем  разделе,  перекрестная
         ссылка между номерами физических страниц и их  адресами сегментов
         получается  от  менеджера  расширенной памяти посредством функции
         25, "Получить массив физических адресов".
              Когда логическая  страница  отображена  в  физическую,  Ваша
         программа может затем адресовать любые  данные  в  этой  странице
         удаленным указателем.Языковые процессоры,которые генерируют толь-
         ко так называемые программы малых моделей,  могут не поддерживать
         использование 32-битовых (удаленных)   указателей  для  именуемых
         элементов данных. В отсутствие такой поддержки некоторые компиля-
         торы  обеспечивают библиотечную программу,  которая копирует блок
         данных из произвольного сегмента  и  адреса  смещения  в  область
         внутри  единственного 64-Кбайтного сегмента данных программы.  Не

                                      - 7-38 -
         имея даже этого,  Вы можете написать  интерфейсную  программу  на
         языке ассемблера для получения того же результата.
              На рис. 7-3 показана гипотетическая конфигурация расширенной
         памяти 384К,  которая используется двумя программами, электронной
         таблицей и программой буферизации принтера.  Иллюстрируются неко-
         торые  динамические  отношения между программами,  обработчиками,
         логическими страницами и физическими страницами, а именно:
              * Две (или более) независимых программы  могут  пользоваться
         расширенной памятью одновременно без взаимного влияния.
              * У одной программы может быть более одного обработчика спе-
         цификации расширенной памяти, размещенного для нее, - фоновая за-
         дача в примере обладает двумя обработчиками.
              * Последовательно  пронумерованные  логические  страницы  не
         приходится отображать в последовательные  физические  страницы  -
         активная приоритетная программа имеет логические страницы 6, 7, 2
         и 1, отображенные в физические страницы 0-3.

              Функция "Передвинуть/обменять  область памяти" (24), которая
         была добавлена как часть спецификации расширенной памяти  LIM 4.0
         обеспечивает  исчерпывающие средства для управления передвижением
         областей данных,  длиной до 1Мбайта,  между расширенной памятью и
         обычной  памятью.  Данная функция также позволяет передвигать или
         обменивать данные, когда обе указанные области находятся в преде-
         лах расширенной памяти или, когда обе области находятся в обычной
         памяти.
              Подфункция передвижения  (00h)  копирует  содержимое  облас-
         ти-источника в область-приемник.  Если указанные области перекры-
         ваются,  менеджер расширенной памяти выбирает  такое  направление
         передвижения,  чтобы область-приемник получала неповрежденную ко-
         пию области источника. Когда часть области-источника перекрывает-
         ся  целевой  областью  в течение операции передвижения,  менеджер
         расширенной памяти возвратит код состояния,  указывающий  на  это
         (как всегда в регистре AH).
              Подфункция обмена (01h) обменивает местами две области памя-
         ти: любая или обе области могут быть в расширенной памяти или
              В отличие от подфункции передвижения  подфункция  обмена  не
         разрешает задания перекрывающихся областей.
              Удобной чертой обеих подфункций является  то,  что  операции
         передвижения или обмена на меняют контекста текущего отображения.
         Любые логические страницы,  которые Ваша программа могла  отобра-
         зить в кадр страниц, не будут изменяться функцией 25, так что для
         Вашей программы нет необходимости сохранять  контекст отображения
         перед применением данной функции.
              Функция "Передвинуть/обменять  область   памяти"   избавляет
         программиста  от нескольких утомительных программных работ,  свя-
         занных с управлением расширенной памятью,  которые были жизненным
         фактом  в  более  ранних  версиях спецификации расширенной памяти
         LIM. Тем не менее, важно контролировать код состояния, возвращае-
         мый данной функцией.  Существуют 13 различных ошибок, которые мо-
         гут случиться в течение операции передвижения или обмена.

                  Разделение расширенной памяти между программами

              При спецификации расширенной памяти LIM 3.2  ничто  не  пре-
         дотвращало две программы от разделения данных в расширенной памя-
         ти.  Программе требовалось только знать обработчик и номер  логи-
         ческой   страницы   данных,  к  которым  она  хотела  обратиться.

                                      - 7-39 -
         Поскольку программа не  может  знать  apriori,  какой  обработчик
                                                  384Kb расширенной
                                         B   C           памяти
         ЪДДДДДДДДДДї                   ЪДДВДДї   ЪДДДДДДДДДДДДДДДДї Дї
         і          іДДДДДДДДДДДДДДВДДДДі 1і 0і   ГДДДДДДДДДДДДДДДДґ
         і    A     і              і    АДДґ 1і   ГДДДДДДДДДДДДДДДДґ  24
         і          і              і       і 2і   ГДДДДДДДДДДДДДДДДґ
         АДДДДДДДДДДЩ              і       і 3і   ГДДДДДДДДДДДДДДДДґ   С
                                   і       і 4і   ГДДДДДДДДДДДДДДДДґ
     Начальный                     і       і 5і   ГДДДДДДДДДДДДДДДДґ   Т
     сегмент                       і       і 6і   ГДДДДДДДДДДДДДДДДґ
     физической                    і       і 7і   ГДДДДДДДДДДДДДДДДґ   Р
     страницы    D   B  C          і    ЪДДЕДДґ   ГДДДДДДДДДДДДДДДДґ
         CC00H  ЪДДВДДВДДї         і  ЪДі 2і 0і   ГДДДДДДДДДДДДДДДДґ   А
                і 0і 2і 6іДДДДДї   і  і АДДґ 1іДї ГДДДДДДДДДДДДДДДДґ
         D000H  ГДДЕДДЕДДґ     і   і  і  ЪДі 2і і ГДДДДДДДДДДДДДДДДґ   Н
                і 1і 2і 7іДДДї і   і  і  і і 3і і ГДДДДДДДДДДДДДДДДґ
         D400H  ГДДЕДДЕДДґ   і і   і  і  і і 4і і ГДДДДДДДДДДДДДДДДґ   И
                і 2і 2і 2іДДДіДіДДДіДДіДДЩ і 5і і ГДДДДДДДДДДДДДДДДґ
         D800H  ГДДЕДДЕДДґ   і АДДДіДДіДДДДі 6і і ГДДДДДДДДДДДДДДДДґ   Ц
                і 3і 2і 1іДї АДДДДДіДДіДДДДі 7і і ГДДДДДДДДДДДДДДДДґ
                АДДБДДБДДЩ і       і  і    і 8і і ГДДДДДДДДДДДДДДДДґ   Ы
              Кадр страниц і       і  і    і 9і і ГДДДДДДДДДДДДДДДДґ
                           і       і  і ЪДДЕДДґ і ГДДДДДДДДДДДДДДДДґ
                           і       АДДДДі 3і 0і і ГДДДДДДДДДДДДДДДДґ   П
                           і          і АДДґ 1і і ГДДДДДДДДДДДДДДДДґ
         ЪДДДДДДДДДДї      і          і    і 2і і ГДДДДДДДДДДДДДДДДґ   О
         і          і      і          і    і 3і і ГДДДДДДДДДДДДДДДДґ
         і    E     іДДДДДДДДДДДДДДДДДЩ    АДДЩ і ГДДДДДДДДДДДДДДДДґ 16Kb
         і          і      і                    і ГДДДДДДДДДДДДДДДДґ
         АДДДДДДДДДДЩ      АДДДДДДДДДДДДДДДДДДДДЩ АДДДДДДДДДДДДДДДДЩ ДЩ

          Рис.7-3. Моментальный снимок расширенной памяти с приоритетной
        и фоновой задачами. В настоящий момент активна приоритетная задача

         A - программа буферизации принтера (фоновая задача),  владеет об-
             работчиками 1 и 3;
         B - обработчик;
         C - номер логической страницы;
         D - номер физической страницы;
         E - электронная таблица (приоритетная задача),  владеет
             обработчиком 2 в обычной памяти.

         (ки)   будет размещен  для нее менеджером расширенной памяти, две
         программы,  разработанные так, чтобы разделять данные в расширен-
         ной  памяти,  должны  подготовить некоторые средства для передачи
         номеров обработчиков во время выполнения.
              В спецификации  расширенной памяти LIM 4.0 разделение данных
         в расширенной памяти сделано немного более легким путем обеспече-
         ния  возможности  связывания  с обработчиком 8-символьного имени.
         Для поддержки данной способности были введены две функции  менед-
         жера  расширенной  памяти:  "Получить/установить имя обработчика"
         (функция 20) и "Получить каталог обработчика" (функция 21).
              Подфункция 00h функции 20, "Получить имя обработчика", возв-
         ращает 8-символьное имя,  связанное  с  обработчиком,  переданным
         функции.  Подфункция 01h, "Установить имя обработчика", связывает
         8-символьную  строку с указанным номером обработчика.  Не сущест-

                                      - 7-40 -
         вует ограничений на символы, используемые для формирования имени,
         и все 8 символов являются  значащими  (имя  не  является  строкой
         ASCII,  завершающейся  байтом  "Пусто" - нулями).  Обработчик без
         имени приобретет имя,  состоящее из 8 байтов двоичных нулей  (или
         "Пусто"  ASCII,  если Вам это предпочтительнее).  Имя обработчика
         устанавливается в "Пусто" при инициализации менеджера расширенной
         памяти, когда обработчик размещается и  когда обработчик освобож-
         дается. Вы можете изменить имя обработчика в любое время, включая
         сброс его в нули.  Единственное ограничение состоит в том, что не
         разрешается двум обработчикам иметь одинаковые имена.
              Менеджер расширенной памяти обеспечивает функцию 21,  "Полу-
         чить каталог обработчика", для определения того, какой обработчик
         связан с конкретным именем или для обеспечения таблицы имен обра-
         ботчиков,  связанных  с  каждым активным обработчиком. Подфункция
         00h, "Получить каталог обработчика", возвращает эту таблицу в об-
         ласть данных, предоставленную пользователем. Поскольку специфика-
         ция поддерживает до 255 обработчиков,  8-байтовое имя обработчика
         плюс 2-байтовое значение обработчика,  целая таблица может потре-
         бовать до 2550 байтов.  Фактическое число обработчиков, поддержи-
         ваемых менеджером расширенной памяти,  может быть получено с  по-
         мощью  подфункции 02h,  "Получить общее количество обработчиков".
         Умножив это число на 10, получаем размер области, необходимой для
         сохранения каталога обработчиков.  Подфункция 01h, "Искать имено-
         ванный обработчик", обеспечивает для программы поиск обработчика,
         связанного с данным именем, без необходимости просмотра всего ка-
         талога обработчиков или запроса имени,  связанного с каждым номе-
         ром обработчика.

                       Выполнение кода в расширенной памяти

              Всегда было возможно использовать память спецификации расши-
         ренной памяти LIM для хранения и выполнения исполняемого кода, но
         не  всегда  это  было  просто.  Во-первых,  максимальный размер в
         64 Кбайт  кадра  страниц  спецификаций  до 4.0 ограничивал размер
         оверлея,  который мог бы быть активным в данный  момент  времени.
         Также каждому разработчику приходилось разрабатывать полный меха-
         низм связывания, который позволял коду в обычной памяти выполнять
         код, находящийся в расширенной памяти.
              У спецификации расширенной памяти LIM 4.0 есть потенциал для
         смягчения некоторых этих  проблем.  Теперь  могут  поддерживаться
         кадры  страниц,  большие 64К,  хотя менеджеры расширенной памяти,
         написанные для плат,  разработанных для спецификации 3.2, вероят-
         но,  не  смогут  обеспечивать большие размеры кадра страниц.  Для
         способствования отображению и связыванию кодовых объектов в  рас-
         ширенной памяти были введены две новые функции, "Изменить отобра-
         жение страниц и перейти" (22) и "Изменить отображение  страниц  и
         вызвать".
              "Изменить отображение страниц и перейти" отображает нуль или
         более  логических страниц (до максимального количества физических
         страниц,  поддерживаемых менеджером расширенной  памяти)  в  кадр
         страниц и передает управление указанному целевому адресу. В отли-
         чие от любой другой функции менеджера расширенной  памяти  данная
         функция  не возвращает управление команде,  следующей за командой
         "int 67h" (кроме случаев, когда менеджер расширенной памяти обна-
         руживает  ошибку  перед переходом к целевому адресу).  Программа,
         которая получает управление в результате выполнения данной  функ-
         ции  отвечает  за установление собственного связывания по выходе.

                                      - 7-41 -
         Когда целевой адрес  получает  управление,  содержимое  регистров
         процессора и флаги являются такими,  какими они были,  когда было
         выдано прерывание менеджера расширенной  памяти.  Таким  образом,
         программы  могут передавать параметры целевой программе в регист-
         рах.  Контекст отображения,  существовавший перед вызовом  данной
         функции не сохраняется.
              Функция "Сменить отображение  страниц  и  вызвать"  является
         аналогом  удаленной команды CALL 80х86.  Аналогично функции "Сме-
         нить отображение страниц и  перейти"  данная  функция  отображает
         нуль  или  более  логических страниц (до максимального количества
         физических страниц,  поддерживаемого менеджером расширенной памя-
         ти) в кадр страниц и передает управление целевому адресу. Страни-
         цы,  отображенные перед тем,  как происходит передача  управления
         называются новым отображением страниц. В отличие от функции "Сме-
         нить отображение страниц и перейти" целевая  программа возвращает
         управление менеджеру расширенной памяти (и по существу,  програм-
         ме, которая выдала "Сменить отображение страниц и вызвать") путем
         выполнения  команды  удаленного  возврата RETURN.  Когда менеджер
         расширенной памяти вновь получает управление от целевой  програм-
         мы, множество  страниц,  называемое  старым  отображением страниц,
         отображается в кадр страниц и менеджер расширенной памяти  возвра-
         щает управление исходной вызывавшей программе.  Содержимое и ново-
         го   и старого отображения страниц задается вызывавшей программой.
         Регистры вызывавшей программы сохраняются в течение процесса.  Со-
         держимое регистров при входе в целевую программу является тем  же,
         какое было во время выдачи прерывания менеджера расширенной памяти
         вызывавшей программой.
              Данная функция  способна  поддерживать  вложенные  вызовы  -
         программа, в которую вошли посредством "Сменить отображение стра-
         ниц и вызвать", может сама использовать эту функцию. Для сохране-
         ния контекста на каждом уровне вызова менеджер расширенной памяти
         использует  стек  вызывающей программы.  Количество байтов стека,
         необходимое для менеджера расширенной памяти для выполнения  это-
         го,  получается путем применения подфункции 02,  "Получить размер
         пространства стека отображения страниц", функции "Сменить отобра-
         жение страниц и вызвать".

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

              Надлежащим образом  сконструированные программы перед завер-
         шением закрывают файлы и освобождают обычную память, которая была
         размещена из DOS.  Аналогичным образом, ресурсы расширенной памя-
         ти,  размещенные Вашей программой,  должны возвращаться менеджеру
         расширенной памяти перед завершением программы.
              Поскольку он работает независимо от операционной  системы, у
         менеджера расширенной памяти нет способа определения,  когда Ваша
         программа завершилась.  Если Ваша программа не  освобождает  явно
         все страницы расширенной памяти,  которые она размещала перед вы-
         ходом,  следующая программа, которая попытается пользоваться рас-
         ширенной памятью,  может найти, что расширенная память заполнена,
         даже хотя данные в расширенной памяти более не используются.
              Если Вы  намереваетесь писать здравые приложения с использо-
         ванием расширенной памяти, для Вашей программы будет недостаточно
         возвращать ресурсы менеджеру расширенной памяти перед нормальными
         завершениями.  Более совершенное обращение должно включать в себя
         код  для очистки ресурсов расширенной памяти в драйвере прекраще-
         ния программы (Break = Control-C),  драйвере критической ошибки и

                                      - 7-42 -
         драйвере  деления  на  нуль.  Прежде всего обработка этих условий
         требует значительного объема программирования на языке  ассембле-
         ра,вместе со способностью разобрать" Техническое справочное руко-
         водство по DOS (DOS Technical Reference Manual)". Правда, недавно
         в некоторые продукты языков высокого уровня,  включая С 5.0 фирмы
         Microsoft и Турбо-Паскаль 4.0 и Турбо-C фирмы Borland,  были вве-
         дены  средства для обработки этих условий в самих языках высокого
         уровня.  У программистов, пользующимся этими продуктами для напи-
         сания  приложений спецификации расширенной памяти,  более нет ра-
         зумных оснований истолковывать процедуры ненормальных завершений.

                         Системное программное обеспечение

              Набор представленных до сих пор функций удовлетворяет нуждам
         управления  расширенной  памятью обычных (нерезидентных) программ
         DOS.  В среде DOS нерезидентная (transient) относится к  програм-
         мам, которые выполняются из подсказки DOS или, которые вызываются
         из  другой  такой  же  программы  посредством  применения функции
         DOS EXEC.  Память,  занимавшаяся такими программами, возвращается
         операционной системе, когда программа завершается, и данная прог-
         рамма должна повторно загружаться в память перед тем, как войти в
         нее вновь.
              Как упоминалось ранее,  драйверы устройств, программы обслу-
         живания прерываний и программы,  остающиеся в памяти после завер-
         шения,  которые пользуются расширенной памятью, обладают дополни-
         тельной ответственностью относительно пользования  ею.  Эти  виды
         программ определяются как резидентные, так как они остаются в па-
         мяти даже после выхода из них в первый раз,  и,  следовательно, в
         них возможно войти повторно в любой момент - даже, когда выполня-
         ется другая программа.

                  Сравнение нерезидентных и резидентных программ

              Резидентность или нерезидентность в памяти в действительнос-
         ти  не является важным вопросом при использовании расширенной па-
         мяти. Существенная разница состоит в том, что нерезидентные прог-
         раммы   выполняются   синхронно,   то   есть   явно   по  запросу
         пользователя.  DOS управляет переходом между программами, так что
         состояние машины и операционная система - управляемые ресурсы яв-
         ляются управляемыми надлежащим образом.
              Программы обслуживания  прерываний  (включая  встроенные   в
         драйверы устройства и в программы,  остающиеся в памяти по завер-
         шении) получают управление асинхронно, откликаясь на аппаратурные
         события.  DOS не играет никакой роли в переходе между выполняемой
         в данный момент программой и программой  обслуживания прерывания.
         Таким образом,  отдельная программа обслуживания прерывания отве-
         чает за сохранение состояния процессора перед его изменением и за
         восстановление  исходного  состояния  перед  возвратом управления
         прерванной программе.  У менеджера расширенной памяти также  есть
         состояние,  на которое часто ссылаются как на контекст. Программы
         обслуживания прерываний,  использующие расширенную память, должны
         сохранять  этот  контекст перед тем,  как изменить его,  и должны
         восстанавливать его перед выходом.
              Заметная доля набора функций спецификации расширенной памяти
         4.0 посвящена требованиям резидентных программ, операционных сис-
         тем и рабочих сред. Кроме того, в функции, требующиеся для сохра-
         нения  и  восстановления  контекста  менеджера расширенной памяти

                                      - 7-43 -
         спецификации расширенной памяти 4.0, также были введены несколько
         функций, специально разработанных для обеспечения совместной ком-
         поновки между операционными системами или  многозадачными средами
         (OS/E на языке спецификации расширенной памяти) и менеджером рас-
         ширенной памяти.  Это компоновка должна была бы  позволить  прог-
         раммному обеспечению операционных систем и многозадачных сред ис-
         пользовать  средства,  которые  могут  быть  включены   в   новые
         разработки аппаратуры спецификации расширенной памяти,  такие как
         механизмы быстрого переключения задач и неразрушаемая память.
              С нашим  пониманием идей расширенной памяти,  представленных
         до сих пор в данной главе,  мы теперь может  рассматривать  более
         сложные  средства  спецификации  расширенной памяти LIM,  которые
         поддерживают резидентные  программы  и  программное  обеспечение,
         ориентированное системно.

                 Обнаружение наличия менеджера расширенной памяти

              Драйверам устройств,  которые  загружаются до того,  как DOS
         полностью инициализирована,  не полагается выдавать вызовы файло-
         вой системы DOS.  Большинство вызовов DOS также являются неразре-
         шенными для резидентных программ,  которые не обрабатывают специ-
         альным  образом  проблему отсутствия повторного входа DOS.  Таким
         образом, метод открытого обработчика, представленный ранее в дан-
         ной  главе,  не  является подходящим средством для этих программ,
         чтобы выявлять наличие расширенной памяти.
              Альтернативный метод,  который может применяться любой прог-
         раммой - метод получения вектора прерывания. Данный метод работа-
         ет следующим образом:
              1. Выдается функция DOS "Получить вектор"  (прерывание  21h,
         функция  35h) для получения адреса программного прерывания менед-
         жера расширенной памяти (67h).
              2. Менеджер  расширенной  памяти  находится  внутри драйвера
         символьного устройства DOS, у которого заголовок устройства нахо-
         дится  по  нулевому смещению в сегменте,  возвращаемом в регистре
         ES,  предыдущим шагом. У всех драйверов символьных устройств есть
         8-символьное поле имени устройства, размещающееся по смещению 0Ah
         в заголовке устройства,  которое DOS использует для указания уст-
         ройства, когда вызовы файловой системы ссылаются на него. Сравни-
         те имя устройства по смещению 0Ah в сегменте,  возвращенном в ре-
         гистре  ES на шаге 1,  со строкой "EMMXXXX0." (Вспомните,  что на
         это имя устройства была ссылка на вызове открытия, использованном
         как  часть  метода открытого обработчика.) Если строки совпадают,
         менеджер расширенной памяти присутствует.

                               Управление контекстом

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

                                      - 7-44 -
         программа  должна сохранять контекст менеджера расширенной памяти
         перед вызовом любой функции,  которая может  его  изменить,  и  -
         восстанавливать исходный контекст перед передачей управления.
              В спецификации расширенной памяти LIM 3.2  предусмотрены два
         набора функций для данной цели. Наиболее проста для использования
         пара функций 8/9,  именуемых "Сохранить  отображение  страниц"  и
         "Восстановить  отображение страниц".  Первая   для данного номера
         обработчика   сохраняет текущий контекст во внутренней для менед-
         жера расширенной памяти области; внутри Вашей программы памяти не
         требуется.  Последняя функция  для данного такого же номера обра-
         ботчика   восстанавливает  контекст,  ранее запомненный для этого
         обработчика во внутренней области сохранения менеджера  расширен-
         ной памяти.  Хотя ими несложно пользоваться,  у этих функций есть
         некоторые ограничения,  которые ведут к рекомендации избегать  их
         применения в новых программах.
              Первое ограничение заключается в том,  что для каждого обра-
         ботчика обеспечивается максимальная область сохранения, а некото-
         рые менеджеры расширенной памяти не поддерживают область сохране-
         ния для всех возможных обработчиков. Результат состоит в том, что
         Ваша программа перестает быть полностью реентерабельной,  если  в
         ней для сохранения и восстановления контекста менеджера расширен-
         ной памяти используются эти функции,  поскольку каждое сохранение
         для данного обработчика должно сопровождаться восстановлением для
         этого же обработчика,  перед тем как  отображение  страниц  может
         быть  вновь  сохранено  с помощью того же обработчика.   Еще одно
         ограничение состоит в том, что эти функции сохраняют и восстанав-
         ливают контекст четырех физических страниц, определенных в специ-
         фикации расширенной памяти LIM 3.2.
              Для преодоления  этих ограничений в спецификации расширенной
         памяти LIM 3.2 имеется функция 15  "Получить/установить отображе-
         ние  страниц".  В отличие функций 8 и 9, которые сохраняют и вос-
         станавливают контекст из внутренней области менеджера расширенной
         памяти,  данная  функция  сохраняет и восстанавливает контекст из
         области,  обеспечиваемой вызывающей программой.  Подфункция   00h
         "Получить  отображение  страниц",  запоминает  контекст менеджера
         расширенной памяти в буфере пользователя,  указываемом регистрами
         ES:DI. Подфункция 01h  "Установить отображение страниц"  загружа-
         ет контекст менеджера расширенной памяти из  буфера пользователя,
         указываемом регистрами DS:SI. Подфункция 02h  "Получить и устано-
         вить отображение страниц"  делает то, что подразумевает ее наиме-
         нование, сохраняя контекст менеджера расширенной памяти в буфере,
         указываемом ES:DI, и загружая новый контекст из области, указыва-
         емой DS:SI. Не следует делать никаких допущений о размере буфера,
         необходимого для получения сохраненного контекста.  Получайте его
         от менеджера расширенной памяти с помощью  подфункции 03h  "Полу-
         чить размер массива отображения страниц". Формат области сохране-
         ния  контекста  зависит от внутренней реализации менеджера расши-
         ренной памяти и не предназначен для понимания  Вашей  программой.
         Даже  предполагая, что  Вы  можете определить положение регистров
         отображения страниц внутри области сохранения,  Вы не можете  на-
         дежно  определить,  какие  логические  страницы были отображены в
         каждую физическую страницу.
              Поскольку спецификация  расширенной памяти LIM 4.0 поддержи-
         вает до 36 физических страниц,  затраты памяти  на  сохранение  и
         восстановление  полного контекста могут существенно превысить то,
         что имело место для 64-Кбайтного кадра страниц спецификации  рас-
         ширенной памяти LIM 3.2.  Для сокращения этих затрат была опреде-

                                      - 7-45 -
         лена функция 16  "Получить/установить частичное отображение стра-
         ниц".  Программа  может  сохранить контекст только для конкретных
         физических страниц,  которые она будет изменять,  аналогично спо-
         собности программиста на языке ассемблера сохранять только те ре-
         гистры,  которые будут изменены программой обслуживания  прерыва-
         ния.  Подфункция  00h    "Получить частичное отображение страниц"
         сохраняет нуль или более регистров отображения страниц  в буфере,
         поставляемом пользователем. Подфункция 01h  "Установить частичное
         отображение страниц"   восстанавливает нуль или более  таких  ре-
         гистров.  Подфункция  02h   "Получить  размер  области сохранения
         отображения страниц"   возвращает размер области сохранения, тре-
         буемый  для сохранения контекста,  состоящего из указанного коли-
         чества физических страниц.

                                Переключение задач

              Явная поддержка переключения задач с помощью менеджера  рас-
         ширенной  памяти была добавлена в спецификации расширенной памяти
         LIM 4.0. Нижеописанный набор функций предназначен для использова-
         ния операционными системами или операционными средами такими, как
         DESQView фирмы Quarterdeck или  Windows  фирмы  Microsoft,  и  не
         должны  использоваться типичными прикладными программами специфи-
         кации расширенной памяти. Конкретные детали, касающиеся использо-
         вания этих функций,  выходят за рамки данной главы,  но некоторое
         обсуждение их целей и реализации целесообразно.
              Функции операционных  сред будут способны использовать преи-
         мущества усовершенствованных  аппаратурных  средств  спецификации
         расширенной памяти.  Одно из средств, которое может быть включено
         в новое поколение плат спецификации расширенной  памяти,  -  мно-
         жество  наборов регистров отображения.  Этим будет обеспечиваться
         практически мгновенное переключение контекста между двумя или бо-
         лее  задачами путем назначения разных наборов регистров отображе-
         ния для каждого контекста.  Еще одно средство, именуемое  "наборы
         регистров  ПДП", позволит многозадачным операционным системам пе-
         реключать задачи,  пока другая задача ожидает завершения передачи
         по ПДП. Поддержка многих наборов регистров отображения и одновре-
         менных передач ПДП включена в девять подфункций функции 28   "Из-
         менить набор регистров отображения".
              Программное обеспечение операционных сред  может определять,
         какие новые возможности аппаратуры поддерживаются данным менедже-
         ром расширенной памяти,  путем выдачи функции 26 "Получить инфор-
         мацию об аппаратуре расширенной памяти". Она возвращает количест-
         во  альтернативных   наборов   регистров   отображения,   наборов
         регистров  ПДП и индикатор способности аппаратуры расширенной па-
         мяти обнаруживать,  когда выполняется ПДП.  Также она  возвращает
         размер  исходных  страниц,  поддерживаемый менеджером расширенной
         памяти.
              Аппаратура, содержащая в себе эти средства,  только начинает
         появляться на рынке.  Чтобы позволить выполнять разработку много-
         задачного программного обеспечения до того, как аппаратура нового
         поколения станет легко доступной,  в спецификации расширенной па-
         мяти LIM 4.0 обеспечена программная имитация альтернативных набо-
         ров регистров отображения путем сохранения и  восстановления  об-
         ластей сохранения контекста, которая обеспечивается многозадачным
         монитором и находится внутри него.

                                      - 7-46 -
                               Неразрушаемая память

              Две функции,  добавленные в спецификации расширенной  памяти
         LIM 4.0, поддерживают сохранение расширенной памяти в течение пе-
         резапуска из памяти.  Программное обеспечение, которое отображает
         память  в  отображаемую  обычную память (память ниже 640К) должно
         перехватывать все условия,  ведущие к перезапуску из памяти (пре-
         рывание 19h BIOS), и выдавать функцию 29  "Подготовить аппаратуру
         расширенной памяти для перезапуска из памяти".  Платы расширенной
         памяти  с соответствующим оборудованием смогут сохранить содержи-
         мое отображаемой обычной памяти,  так же как и  текущий  контекст
         отображения на время перезапуска из памяти. Менеджеры расширенной
         памяти для существующих плат не реализуют данную опцию, поскольку
         платы зависят от схемы регенерации системной памяти, которая бло-
         кируется на время перезапуска из памяти.
              Функция 19  "Получить/установить  атрибут обработчика"  поз-
         воляет приложению определить,  поддерживает ли менеджер расширен-
         ной памяти возможность сохранения содержимого страниц обработчика
         в течение перезапуска из памяти. Если да, приложение может запро-
         сить,  чтобы  менеджер  расширенной  памяти или сохранил страницы
         указанного обработчика на время перезапуска из памяти путем уста-
         новки атрибута обработчика на неразрушаемость, или разрешил   ме-
         неджеру расширенной памяти освободить обработчик и  сбросить  со-
         держимое   связанных  страниц  во  время  перезапуска  из  памяти
         (разрушаемый обработчик). По умолчанию у всех обработчиков внача-
         ле атрибут установлен на разрушаемость.

                                Управление доступом

              Важной особенностью набора функций операционных сред являет-
         ся то,  что многозадачный менеджер может запретить доступ к функ-
         циям менеджера расширенной памяти,  ориентированным на операцион-
         ную систему,  для любой программы, кроме самого себя. Функция  30
         "Разрешить/запретить  набор функций операционных сред"  позволяет
         многозадачному менеджеру блокировать функции 26,  28 и  30  перед
         тем,  как он передаст управление прикладному программному обеспе-
         чению и разблокировать доступ для собственных нужд.

                                Заключение

              Для многих типов приложений  расширенная  память  предлагает
         практическое программное решение для 640-Кбайтного ограничения PC
         DOS.  Наиболее современным определением  программного  интерфейса
         между  приложением и механизмом управления памяти,  переключаемой
         банками,  является спецификация расширенной памяти LIM 4.0.  Этот
         программный интерфейс реализует менеджер расширенной памяти,  ко-
         торый обычно загружается как драйвер символьного  устройства  DOS
         во  время загрузки.  Системы расширенной памяти могут строиться с
         помощью сложного оборудования  спецификации  расширенной  памяти,
         механизма страниц процессора 80386 Intel или регистрами отображе-
         ния на некоторых типах плат расширения памяти  PS/2  или  системы
         расширенной памяти могут быть чисто программными.
              Приложения выдают запросы функций для  менеджера расширенной
         памяти через механизм программного прерывания 67h,  сходным обра-
         зом с интерфейсом прерывания 21h DOS.  Параметры передаются через
         регистры и/или структуры данных,  резидентные в памяти, механизм,
         наиболее естественный для программистов на языке ассемблера. При-

                                      - 7-47 -
         ложения,  написанные на языках высокого уровня, также могут обра-
         щаться к расширенной памяти,  если эти языки обеспечивают способы
         выдачи  программных прерываний,  обработки регистров процессора и
         определяют удаленные указатели для кодовых объектов и данных.
              Спецификация расширенной  памяти LIM 3.2 определяет 8-Мбайт-
         ное расширенное адресное пространство,  разбитое  на  16-Кбайтные
         страницы. До 64К из этого пространства может быть доступно однов-
         ременно через 64-Кбайтный кадр страниц,  размещенный в пространс-
         тве памяти над 640К. Также определяется набор из 14 функций отно-
         сительно низкого уровня,  которые могут применяться для доступа и
         обработки кодовых объектов и данных в расширенной памяти.  Эти 14
         функций могут быть разделены на три группы:  информационные,  уп-
         равления данными и управления контекстом.
              Спецификация расширенной памяти LIM 4.0 -  дополнение,  сов-
         местимое вверх,  спецификации 3.2,  которое сейчас включается как
         часть в MS-DOS версии 4.0.  Она содержит несколько средств,  при-
         сутствующих в усовершенствованной спецификации расширенной памяти
         AQA,  включая кадр страниц,  больший 64К, и способность поддержи-
         вать  отображаемую  память  ниже 640К.  Она также добавляет класс
         функций,  разработанных, чтобы непосредственно поддерживать быст-
         рое переключение задач многозадачными операционными средами. Спе-
         цификация 4.0 увеличивает адресное пространство расширенной памя-
         ти  до 32 Мбайт и предлагает набор функций более высокого уровня,
         чем те, которые были возможны в спецификации 3.2.
              Пользоваться расширенной  памятью  могут  как нерезидентные,
         так и резидентные приложения. Резидентные приложения должны поль-
         зоваться функциями управления контекстом для сохранения и восста-
         новления контекста менеджера расширенной памяти,  так  как  такие
         программы должны сохранять состояние процессора при входе и  вос-
         станавливать это состояние при выходе.
              Функции поддержки операционной системы спецификации 4.0 спо-
         собны пользоваться преимуществами  усовершенствованных аппаратных
         средств,  которые могут появиться в новых разработках расширенной
         памяти. Одним из таких средств является множество наборов отобра-
         жения страниц, которое позволяет многозадачной операционной среде
         почти мгновенно переключать контекст путем назначения набора  ре-
         гистров  отображения задаче.  Другое средство - неразрушаемая па-
         мять - позволит содержимому  расширенной  памяти  сохраняться  во
         время  перезапуска  из памяти.  Платы,  обеспечивающие аппаратную
         поддержку для этих функций, только теперь появляются в продаже.

                                    Литература

              Duncan, Ray.  "Lotus/Intel/Microsoft Expanded Memory",  Byte,
         11, no.11, 1986 (Специальное издание IBM).
              Как писать программы с помощью спецификации расширенной  па-
         мяти LIM 3.2.  Даны примеры - части программы RAMDISK,  в которой
         используется расширенная память.

              Hansen, Marion,  and  John  Driscoll.  "LIM   EMS   4.0:   A
         definition for the Next Generation of Expanded Memory", MSJ3, no.
         1, Jan.88.
              Описание средств, введенных в спецификации расширенной памя-
         ти LIM 4.0. Примеры программ на Си и языке ассемблера  показывают
         улучшенные методы для сохранения экрана,  разделения данных между
         программами и выполнения кода из расширенной памяти.


                                      - 7-48 -
              Hansen, Marion,  Bill Krueger,  and Nick Stueklen. "Expanded
         Memory:  Writing  Programs  That  Break the 640K Barrier",  MSJ2,
         no.1, Mar.87.
              Описание спецификации  расширенной  памяти LIM 3.2.  Примеры
         программ на С и языке ассемблера показывают, как выполнять сохра-
         нение экрана и исполнять код из расширенной памяти.

              Lefor, John  A.,  and  Karen  Lund.  "Reaching into Expanded
         Memory", PCTJ5, no.5, May 86.
              Рассмотрение спецификации  расширенной памяти LIM 3.2 и усо-
         вершенствованной спецификации расширенной памяти AQA,  ориентиро-
         ванное на приложения. Примеры законченных программ, которые полу-
         чают параметры  расширенной  памяти  и  распечатывают  данные  из
         расширенной памяти.

              Lotus/Intel/Microsoft. "Lotus/Intel/Microsoft       Expanded
         Memory Specification,  Version 4.0",  Document number  300275-05,
         Oct 87.
              Полная спецификация самой последней версии спецификации рас-
         ширенной  памяти.  Включает  примеры  программ на Турбо-Паскале и
         языке ассемблера.

              Mirecki, Ted.  "Expandable Memory", PCTJ4 no.2, Feb 86.
              Описание спецификации  расширенной  памяти LIM 3.2 и усовер-
         шенствованной спецификации расширенной памяти AQA.  Тесты изделий
         расширенной памяти фирм Intel и AST.

              Yao, Paul.  "EMS  Support  Improves  Microsoft  Windows  2.0
         Application Performance", MSJ3, no.1, Jan 88.
              Техническое рассмотрение  способа,  в  котором  в  программе
         Windows 2.0 используется спецификация расширенной памяти  LIM 4.0
         для поддержания многих параллельно работающих приложений.



                                      - 7-49 -

             Программы сопряжения на низком уровне и пример приложения

              Если данная глава выполнила свое назначение, то у Вас должно
         быть хорошее представление того, как работает расширенная память,
         и  как  программы DOS могут использовать мегабайты дополнительной
         памяти для кодов и данных. Теперь мы поможем Вам применять потен-
         циал расширенной памяти в Вашем собственном программном обеспече-
         нии  путем  снабжения  исчерпывающим набором листингов на языке С
         фирмы Microsoft версия 5.0. Мы также представим пример приложения
         спецификации расширенной памяти, в котором используются некоторые
         из более сложных функций расширенной памяти. В таблице 7-4 кратко
         перечислены листинги, которые Вы найдете в конце данной главы.

                                                         Таблица 7-4
                       Программы сопряжения на низком уровне
         ДДДВДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
        Лис-іИмя файла і                      Содержание
        тингі          і
         ДДДЕДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
         7-2іEMMCONST.HіОбеспечивает #defines для общих  констант менед-
            і          іжера расширенной памяти
         7-3іEMMTYPES.HіСодержит typdefs (определения типов)  для структур
            і          іданных, передаваемых между функциями сопряжения
            і          іспецификации расширенной памяти  и  прикладными
            і          іпрограммами спецификации расширенной памяти
         7-4іEMMERMSG.CіДает массив символьных строк,обеспечивающих  крат-
            і          ікое  текстовое  описание для каждого ненулевого
            і          ікода состояния функции спецификации расширенной
            і          іпамяти
         7-5іEMMFUNC.C іИсчерпывающая библиотека функций спецификации  рас-
            і          іширенной памяти. Если не определено иное, любая
            і          іфункция спецификации расширенной памяти возвра-
            і          іщает  код состояния функции спецификации расши-
            і          іренной памяти как целое
         7-6іEMMFUNC.H іСодержит  прототипы  функций  для каждой функции в
            і          іEMMFUNC.С.  Если  Ваш  компилятор  поддерживает
            і          іпрототипы функций,  определенные в спецификации
            і          іязыка С ANSI (как версия  5  фирмы  Microsoft),
            і          івключение данного файла в Ваши приложения будет
            і          ігарантировать, что типы аргументов, указанные в
            і          іВаших  программах  согласуются с типом парамет-
            і          іров, ожидаемым вызванными функциями.
         7-7іEMMEXIST.HіСодержит программы тестирования наличия расширен-
            і          іной памяти.  Метод "открытого обработчика"  вы-
            і          іполняется функцией emm_exists (строка 25).  Ме-
            і          ітод "получения вектора прерывания" тестирования
            і          іналичия расширенной памяти выполняется функцией
            і          іemm_exists2 (строка 113).
         7-8іSNAPSHOT.CіОбеспечивает  программу,  остающуюся  резидентной
            і          іпосле завершения, которая сохраняет текущее со-
            і          ідержимое  экрана  дисплея в буфер в расширенной
            і          іпамяти каждый  раз,  когда  нажимается  клавиша
            і          іPrtScr. Данная программа может сохранять столь-
            і          іко образов экрана, сколько расширенной памяти в
            і          іВашей системе.
         7-9іPLAYBACK.CіОбеспечивает программу для копирования образов эк-
            і          іранов, сохраненных программой SNAPSHOT в расши-
            і          іренной памяти, в стандартный выходной файл DOS.
            і          іЭкранные изображения программы мо

                                      - 7-50 -
            і          ігут захватываться  постоянно,  например,  путем
            і          іперенаправления  стандартного  выходного  файла
            і          іPLAYBACK в дисковый файл.
        7-10іBEEP.ASM  іУтилита для издания звука слышимого тона на встро-
            і          іенном динамике PC.
         ДДДБДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

                               О примере приложения

              Пример приложения  состоит  из  двух программ:  SNAPSHOT.C и
         PLAYBACK.C. SNAPSHOT.C - программа, остающаяся в памяти резидент-
         но после завершения, которая сохраняет образы текстовых экранов в
         расширенной  памяти  путем  перехвата  прерывания  печати  экрана
         (int 5).  Данное прерывание выдается всякий раз, когда нажимается
         клавиша PrtScr или Print Screen.  SNAPSHOT также строит индексную
         структуру данных в расширенной памяти, которая содержит номер ло-
         гической страницы и смещение в байтах для каждого  образа экрана,
         сохраненного в расширенной памяти.  PLAYBACK просто считывает ин-
         дексную структуру данных,  копируя каждый из текстовых экранов из
         расширенной памяти в стандартный выходной файл DOS.  На рис.  7-4
         показано использование расширенной памяти для  установления связи
         между двумя независимыми программами.
              Для того чтобы продемонстрировать некоторые из более сложных
         идей расширенной памяти,  которые были представлены в данной гла-
         ве,  данное приложение с необходимостью является  более  сложным,
         чем мог бы ожидать программист среднего уровня от первого проекта
         приложения расширенной памяти.  Разработчики, которые незнакомы с
         программами, остающимися резидентными по завершении, и программи-
         рованием драйверов прерываний в среде DOS, могут ощущать особенно
         значительные    трудности,   воспринимая   большую   часть   кода
         SNAPSHOT.C, которая требуется для установки, управления и заверше-
         ния самой программы.
              В то же время в программе SNAPSHOT - не только доля усложне-
         ний,  связанных с DOS, использование переключения контекста и но-
         вых функций спецификации расширенной памяти  LIM  4.0  предлагает
         ценный  пример для изучения идей спецификации расширенной памяти,
         которые не всегда демонстрируются в руководствах по  программиро-
         ванию спецификации расширенной памяти. Ключевые средства специфи-
         кации расширенной памяти,  использованные в SNAPSHOT,  включают в
         себя:
              * Разделение расширенной памяти между программами  с помощью
         средства "Именования обработчика" (функция 20 спецификации расши-
         ренной памяти).
              * Переключение   контекста  с  помощью  "Получить/установить
         отображение страниц" (функция 15 спецификации  расширенной  памя-
         ти).
              * Перемещения блоков данных между обычной и расширенной  па-
         мятью  с  помощью  функции  "Передвинуть/обменять область памяти"
         (функция 18 спецификации расширенной памяти).
              * Динамическое  добавление  логических страниц к ранее разме-
         щенным для  обработчика спецификации расширенной памяти с помощью
         функции переразмещения страниц (функция 18 спецификации расширен-
         ной памяти).
              Примечание: Для выполнения этих программ Ваш менеджер расши-
         ренной памяти должен поддерживать спецификацию расширенной памяти
         LIM 4.0.


                                      - 7-51 -
                       Несколько соображений по кодированию

              Фактические действия  по сохранению экранов видеоотображения
         в расширенной памяти  тривиальны.  Единственного  вызова  функции
         спецификации расширенной памяти "Передвинуть/обменять область па-
         мяти" на строке 175 SNAPSHOT.C достаточно для  копирования  всего
         образа  экрана  из видеобуфера в расширенную память.  Так как это
         происходит внутри обработчика прерывания, функция "Получить отоб-
         ражение страниц" на строке 110 требуется для сохранения контекста
          SNAPSHOT.EXE ("после завершения остаться в памяти")
          ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
          і ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї і
          і іinst_isr(вход при нажатии PrtScr)і і
          і і 1.Сохраняет состояние emm       і і
          і і 2.Копирует образ экрана         і і
          і і   в расширенную память          і і
          і і 3.Обновляет индекс образа эк-   і і
          і і   рана в расширенной памяти     і ГДДДї
          і і 4.Восстанавливает состояние emm і і   і
          і АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ і   і
          і ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї і   і
          і іmain                             і і   і
          і і 1.Начинает,завершает и сооб-    і і   і
          і і   щает состояние программы      і і   і
          і АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ і   і
          АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ   і
                                                    і  Расширенная память
                               SNAPSHOT             і
                               ЪДДДДДДДДДДДДДДВДДДДДБДДДДДДВДДДДДДДДДДДДї
                               і Заголовок и  іОбраз экранаіОбраз экранаі
                             0 і индекс экранаі     1      і     2      і
                               ГДДВДДДДДДДДДДДБВДДДДДДДДДДДБДВДДДДДДДДДДґ
                               і  іОбраз экранаі   . . .     і  . . .   і
                             1 і  і     3      і             і          і
                               ГДДБДВДДДДДДДДДДБДДДДДДДДДДДДДБДДДДДДДДДДґ
                               і    і                                   і
                             2 і    і                                   і
                               ГДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                               і                                        і
                             3 і                                        і
                               ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                               і                                        і
                             4 і                                        і
                               ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                               і                                        і
                             5 і                                        і
                               АДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДЩ
                                                    і
          PLAYBACK.EXE                              і
          ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї       і
          і1.Обнаруживает расширенную памятьі       і
          і2.Ищет идентификатор обработчика і       і
          і3.Отображает в индекс экрана     і       і
          і4.Отображает в каждый образ эк-  ГДДДДДДДЩ
          і  рана и копирует в стандартный  і
          і  выходной файл                  і
          АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
              Рис. 7-4. Пример приложения  - SNAPSHOT/PLAYBACK

                                      - 7-52 -
         отображения спецификации расширенной памяти.  Функция "Установить
         отображение  страниц"  на  строке 120 восстанавливает отображение
         страниц спецификации расширенной памяти,  которое имело место пе-
         ред входом в драйвер прерывания.
              Функция "Установить имя обработчика" на строке 372 SNAPSHOT.
         C  связывает имя в кодах ASCII "SNAPSHOT" с обработчиком специфи-
         кации расширенной памяти,  который данной программой размещен для
         сохранения  экрана.  Путем  задания  обработчику  имени программа
         PLAYBACK может определить положение и обратиться к данной области
         расширенной  памяти,  не зная действительного номера обработчика,
         значение которого может изменяться  каждый  раз,  когда  стартует
         SNAPSHOT.



                             Листинг 7-2. EMMCONST.H
        ------------------------------------------------------------------
        /*
        Общие константы спецификации расширенной памяти
        */

        #define EMM_INT             0x67        /*программное прерывание
                                                    расширенной памяти*/
        #define HANDLE_NAME_LENGTH  8           /*кол-во байт в имени об-
                                                  работчика*/
        #define PAGE_FRAMES         4           /*максимальное кол-во фи-
                                                  зических страниц*/
        #define PAGE_SIZE           16384       /*кол-во байтов в странице
                                         спецификации расширенной памяти*/
        #define EMM_DEVICE          "EMMXXXX0"  /*имя драйвера устройства
                                            менеджера расширенной памяти*/
        #define MAX_HANDLE          255         /*максимальное кол-во обра-
                                    ботчиков менеджера расширенной памяти*/
        /*
        Константы для кодов состояния спецификации расширенной памяти
        */

        #define FRSTEMERR           0x80        /*номер первой ошибки ме-
                                               неджера расширенной памяти*/
        #define LASTEMERR           0xA4        /*номер последн. ошибки ме-
                                               неджера расширенной памяти*/
        #define FUNCCOK             0x00        /*указанная функция завер-
                                                шилась без ошибок*/
        #define EMDRVSWF            0x80        /*программная ошибка драй-
                                        вера менеджера расширенной памяти*/
        #define EMDRVHWF            0x81        /*драйвер менеджера расши-
                                           ренной памяти обнаружил ошибку
                                                      в аппаратуре*/
        #define EMDRVBSY            0x82        /*драйвер менеджера расши-
                                                 ренной памяти      занят
                                                 (других не осталось)*/
        #define HANDLNFD            0x83        /*не найден указанный обра-
                                                  ботчик*/
        #define FUNCCUND            0x84        /*код функции неопределен*/
        #define HANDLINS            0x85        /*нет доступных обработчи-
                                                           ков*/
        #define MAPCXPRO            0x86        /*произошла ошибка восста-

                                      - 7-53 -
                                          новления контекста отображения*/
        #define TOTPGINS            0x87        /*не хватает страниц для
                                                  запроса*/
        #define UNAPGINS            0x88        /*не хватает размещенных
                                                  страниц для запроса*/
        #define LPAGE2SM            0x89        /*нуль логических страниц
                                          был запрошен от  функции,  сов-
                                          местимой  со спецификацией рас-
                                          ширенной памяти LIM 3.2*/
        #define LPAGERNG            0x8A        /*логическая страница вне
                                        диапазона указанного обработчика*/
        #define PPAGE2BG            0x8B        /*физическая страница вне
                                                      диапазона*/
        #define MRCSAFUL            0x8C        /*область сохранения кон-
                                             текста регистров отображения
                                             полна*/
        #define MRCSTDUP            0x8D        /*у стека контекста регист-
                                        ров отображения   уже  есть  кон-
                                       текст, связанный с указанным обра-
                                       ботчиком*/
        #define MRCSTNFD            0x8E        /*у стека контекста регист-
                                        ров отображения   нет  контекста,
                                         связанного с указанным  обработ-
                                         чиком*/
        #define SFUNCUND           0x8F        /*была запрошена неопреде-
                                                  ленная подфункция*/
        #define ATTRBUND            0x90      /*тип атрибута неопределен*/
        #define NVSTGUNS            0x91      /*система не поддерживает не-
                                                        разрушаемость*/
        #define MREGNOVW            0x92      /*во время передвижки области
                                 произошла частичная перезапись источника*/
        #define MRFGN2SM            0x93      /*область спецификации расши-
                                           ренной памяти  слишком  велика
                                                для указанного обработчи-
                                                ка*/
        #define MREGNOVL            0x94      /*область обычной памяти и
                                        область расширенной памяти перек-
                                                            рываются*/
        #define LPGOF2BG            0x95      /*смещение внутри логической
                                        страницы превышает  размер  логи-
                                                 ческой страницы*/
        #define MREGN2BG            0x96      /*длина области превосходит
                                                предел в 1 Мбайт*/
        #define MREGNDUP            0x97      /*область-источник и область-
                                        приемник расширенной памяти имеют
                                        один и тот же обработчик и перек-
                                        ры
                                                      ваются*/
        #define MREGNUND            0x98      /*неопределенный/неподдержи-
                                        ваемый типы   памяти-источника  и
                                                    приемника*/
        #define AMRSNFD             0x9A      /*указанный альтернативный
                                        набор регистров не существует*/
        #define AMDRSINS            0x9B      /*все альтернативные наборы
                                        регистров отображения/ПДП заняты*/
        #define AMDRSUNS            0x9C      /*альтернативные наборы ре-
                                        гистров отображения/ПДП  не  под-

                                      - 7-54 -
                                                            держиваются*/
        #define AMDRSUND            0x9D      /*указанный альтернативный
                                        набор регистров   отображения/ПДП
                                        не определен, не размещен или яв-
                                        ляется текущим набором*/

        #define DDMACUNS            0x9E      /*назначенные каналы ПДП не
                                                поддерживаются*/
        #define DDMACNFD            0x9F      /*назначенный указанный ка-
                                        нал  ПДП не существует*/
        #define HNDVLNFD            0xA0      /*не найдено значение, соот-
                                        ветствующее указанному имени  об-
                                                               работчика*
                                                               /
        #define HNDNMDUP            0xA1      /*обработчик с указанным
                                                 именем уже существует*/
        #define MREGNWRP            0xA2      /*попытка циклического пере-
                                        хода 1-Мбайтного адресного прост-
                                        ранства во время передвижки или
                                                         обмена*/
        #define USRDSFMT            0xA3      /*содержимое структуры дан-
                                        ных пользователя,      переданное
                                        функции искажено или бессмысленно
                                        */
        #define OPSYSACC            0xA4      /*операционная система зап-
                                        рещает доступ к данной функции*/
        ------------------------------------------------------------------


                             Листинг 7-3. EMMTYPES.H
        ------------------------------------------------------------------
        /*
        Структуры, используемые для связи с менеджером расширенной памяти
        */

        #define PCONTEXT            unsigned char
        #define PMAP                unsigned char

        typedef struct handle_page { /*структура  страницы  обработчика*/
                unsigned int emm_handle; /*размещенный обработчик
                                      менеджера расширенной памяти*/
                unsigned int pages_alloc_to_handle; /*логические страницы
                                               принадлежащие обработчику*/
        } HANDLE_PAGE;

        typedef struct ppmap { /*структура запроса частичного контекста*/
                unsigned int seg_cnt; /*количество отображаемых сег-
                                     ментов, которое требуется получить*/
                unsigned int seg_addr[PAGE_FRAMES];  /*адрес отображае-
                              мого сегмента, который требуется получить*/
        } PPMAP;


        typedef struct  log_to_phys  { /*структура отображения логических
                                                 на физические страницы*/

                unsigned int log_page_no;  /*номер логической  страницы*/

                                      - 7-55 -
                unsigned int phys_page_no; /*номер кадра страниц/адрес
                                                  отображаемого сегмента*/
        } LOG_TO_PHYS;

        typedef struct handle_names { /*элемент массива имен обработчика*/
                   unsigned int   handle_value;   /*обработчик*/
                    char handle_name[HANDLE_NAME_LENGTH]; /*имя, связанное
                                                           с обработчиком*/
        } HANDLE_NAMES;

        typedef struct  map_phys_page  { /*отображение отображаемого сег-
                                         мента в номер физическ. страницы
                                         */
                unsigned int phys_page_segment;  /*адрес  сегмента  физи-
                                                                 ческ.
                                                                 страни-
                                                                 цы*/
                unsigned int phys_page_number; /*номер физическ. страницы*/
        } MAP_PHYS_PAGE;

        typedef struct  hardware_info  { /*структура данных об аппаратуре
                                          спецификации расширенной памяти
                                          */
              unsigned int raw_page_size; /*кол-во байт в исх. странице*/
              unsigned int alt_reg_sets; /*кол-во альтернативных на-
                                          боров регистров отображения*/
                 unsigned int ctx_savearea_size;  /*кол-во байт в области
                                                    сохранения контекста*/
                 unsigned int dma_reg_sets;  /*кол-во  наборов  рег-ров
                                                ПДП*/
                 unsigned  int  dma_chan_op;  /*0:  работа  ПДП по
                                                станд. LIM,
                                                1: только один канал ПДП*/
        } HARDWARE_INFO;

        #define CONV_MEM           0        /*обычная память*/
        #define EXP_MEM            1        /*расширенная память*/

        typedef struct mregn { /*дескриптор области памяти*/

          unsigned char memory_type;       /*CONV_MEM / EXP_MEM*/

          unsignedint handle;       /*CONV_MEM: 0, EXP_MEM: обработчик*/

          unsigned int inutial_offset; /*CONV_MEM: 0 -65535,
                                        EXP_MEM:  0  -   16383*/
          unsigned int initial_seg_page;  /*CONV_MEM: адрес сегмента,
                                            EXP_MEM: номер страницы*/
        } MREGN;

        typedef struct move_xchg { /*структура  передвижки/обмена*/
           long region_length;    /*0 - 1 Мбайт*/
           MREGN source;          /*дискриптор области-источника*/
           MREGN dest;            /*дескриптор области -приемника*/
        } MOVE_XCHG;
        -------------------------------------------------------------------


                                      - 7-56 -
                             Листинг 7-4. EMMERMSG.C
        ------------------------------------------------------------------
        /*
        Наименование: emmermsg.c
        Содержание: сообщения  об  ошибках  для кодов ошибок спецификации
                    расширенной памяти LIM 4.0
        Ссылка: Lotus(r)/Intel(r)/Microsoft(r)  спецификация  расширенной
                    памяти, версия 4.0, стр.А5-А10
        */
        char *emmermsg[] = {
         "EMM driver  software  failure",                            ;  1
         "EMM  driver detected hardware failure",                    ;  2
         "EMM driver busy (doesn't happened any more)",              ;  3
         "Cannot  find  the  specified  handle",                     ;  4
         "The function  code  is  undefined",                        ;  5
         "No handles are currently available",                       ;  6
         "A  mapping  context  restoration  error  has occured",     ;  7
         "Insufficient  total  pages  for  request",                 ;  8
         "Insufficient  unallocated  pages  for  request",           ;  9
         "Zero logical pages have been requested from LIM 3.2
          compatible function",                                      ; 10
         "Logical page   out  of  range  for  specified  handle",    ; 11
         "Physical page out of range",                               ; 12
         "Mapping register  context save  area  is  full",           ; 13
         "Mapping  register context stack already has a context      ;
          associated with the specified handle",                     ; 14
         "Mapping register context stack does not have a  context    ;
          associated with the specified handle",                     ; 15
         "Undefined subfunction was  requested",                     ; 16
         "The  attribute type   is  undefined",                      ; 17
         "The  system  does  not  support nonvolatility",            ; 18
         "Partial source overwrite occured during move  region",     ; 19
         "Expanded  memory  region is too big for specified  handle",; 20
         "Conventional memory region and expanded memory region
         overlap",                                                   ; 21
         "Offset within a logical page exceeds the length of
          logical page",                                             ; 22
         "Region length   exceeds  1-Mbyte  limit",                  ; 23
         "Source  and destination expanded memory regions have the
          same handle and overlap",                                  ; 24
         "Undefined/unsupported memory source and destination types",; 25
         "Error code 0x99 is not used",                              ; 26
         "Specified alternate  map register  set  does  not  exist", ; 27
         "All alternate map/DMA register sets are in use",           ; 28
         "Alternate  map/DMA  register sets  are  not  supporte",    ; 29
         "Specified alternate map/DMA register set is not defined,
          not allocated, or is the current one",                     ; 30
         "Dedicated DMA  channels  are  not   supported",            ; 31
         "The specified  dedicated  DMA  channel does not exist",    ; 32
         "No corresponding handle value could be found for the
          specified handle name",                                    ; 33
         "A handle  with  the  specified  name  already  exists",    ; 34
         "Attempt to wrap around 1-Mbyte address space during
          move or exchange",                                         ; 35
         "The contents  of  the  user data structure passed to the
          function were corrupt or meaningless",                     ; 36
         "The operating system denied access to the function"        ; 37

                                      - 7-57 -
             };
        -------------------------------------------------------------------

        1 - программная ошибка драйвера менеджера расширенной памяти, 2 -
        драйвер менеджера расширенной памяти обнаружил ошибку в аппарату-
        ре, 3 - драйвер менеджера расширенной памяти занят (других не ос-
        талось),  4 - не найден указанный обработчик, 5 - код функции не-
        определен,  6 - нет доступных обработчиков,  7 - произошла ошибка
        восстановления контекста отображения,  8 - не хватает страниц для
        запроса,  9 - не хватает размещенных страниц для  запроса,  10  -
        нуль  логических страниц был запрошен от функции,  совместимой со
        спецификацией расширенной памяти LIM 3.2, 11 - логическая страни-
        ца вне диапазона указанного обработчика,  12 -физическая страница
        вне диапазона,  13 - область сохранения контекста регистров отоб-
        ражения  полна,  14 - у стека контекста регистров отображения уже
        есть контекст,  связанный с указанным обработчиком,  15 - у стека
        контекста регистров отображения нет контекста,  связанного с ука-
        занным обработчиком,  16 - была запрошена неопределенная подфунк-
        ция, 17 - тип атрибута неопределен, 18 - система не поддерживает
        неразрущаемость,  19 - во время передвижки области произошла час-
        тичная перезапись источника,  20 - область спецификации расширен-
        ной памяти слишком велика для указанного обработчика,  21  -  об-
        ласть  обычной памяти и область расширенной памяти перекрываются,
        22 - смещение внутри логической страницы превышает  размер  логи-
        ческой страницы, 23 - длина области превосходит предел в 1 Мбайт,
        24 - область-источник и область-приемник расширенной памяти имеют
        один и тот же обработчик и перекрываются, 25 - неопределенный/не-
        поддерживаемый типы памяти-источника и приемника, 26 - код ошибки
        0х99 не используется, 27 - указанный альтернативный набор регист-
        ров не существует, 28 - все альтернативные наборы регистров отоб-
        ражения/ПДП заняты,  29 - альтернативные наборы регистров отобра-
        жения/ПДП не поддерживаются,  30 - указанный альтернативный набор
        регистров отображения/ПДП не определен,  не размещен или является
        текущим набором, 31 -назначенные каналы ПДП не поддерживаются, 32
        - назначенный указанный канал ПДП не существует,  33 - не найдено
        значение,  соответствующее указанному имени обработчика, 34 - об-
        работчик  с указанным именем уже существует,  35 - попытка цикли-
        ческого перехода 1-Мбайтного адресного пространства во время  пе-
        редвижки   или   обмена,   36   -   содержимое  структуры  данных
        пользователя,   переданное  функции  искажено  или  бессмысленно,
        37 - операционная система запрещает доступ к данной функции


                              Листинг 7-5. EMMFUNC.C
        ------------------------------------------------------------------
        #include 
        #include "emmconst.h"
        #include "emmtypes.h"
        #pragma check_stack(off)
        #define CONTINUE_COL 32    /*колонка продолж-я сообщ-я об ошибке*/
        static union REGS inregs, outregs;
        static struct SREGS segregs;
        static unt result;
        void ShowEMMErr(errcode, lineno, filename)
        unsigned int errcode;
        unsigned int lineno;
        char *filename;

                                      - 7-58 -


        {
             unsigned int ec, func, len, line;
             char *bp, *lp, *cp;
             extern char *emmermsg[];

             ec = errcode & 0x00FF;
             func = inregs.x.ax;
             printf("EMM error detected at line (%d) in source file(%s)\n",
                     lineno, filename);
             if (ec < FRSTEMER || ec > LASTEMER)
                printf("EMM Function (%04X) Error(%02X): Unknown Error
                   Code!\n", func, ec);
             else {
                printf("EMM Function (%04X) Error(%02X): ", func, ec);
                lp = emmermsg[ec-FRSTEMERR];
                line = 0;
                while (*lp) {
                   for (cp = lp, len = 80 - CONTINUE_COL; *cp && len;
                      cp++, len --)
                         if (*cp == ' ')
                            bp = cp;
                   if (*cp)
                      *bp++ = '\0';
                   if (line++)
                      printf("                                      ");
                   printf("%s\n", lp);
                   lp = (*cp) ? bp : cp;
                }
            }
        }

        EMGGetStatus() /*тестирует наличие работающего менеджера расширен-
                              ной памяти*/
        {
             inregs.h.ah = 0x40;      /*функция "Получить состояние" спе-
                                        цификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             return(result);
        }

        EMSGetFrameAddr(pfa)          /*возвращает удаленный адрес кадра
                                   страниц менеджера расширенной памяти*/
        char far **pfa;
        {
             inregs.h.ah = 0x41;      /*функция "Получить адрес кадра
                                   страниц" спецификации расширенной памя-
                                                         ти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             if (!result) {
                FP_SEG(*pfa) = outregs.x.bx;
                FP_OFF(*pfa) = 0;
             }
             return(result);
        }


                                      - 7-59 -
        EMSGetPageCnt(una, tot)       /*возвращает кол-во общих и неразме-
                                   щенных страниц спецификации расширенной
                                                               памяти*/
        unsigned int *una, *tot;
        {
             inregs.h.ah = 0x42;      /*функция "Получить кол-во неразме-
                                   щенных страниц" спецификации расширен-
                                                   ной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             if (!result) {
                        *una = outregs.x.bx;
                        *tot = outregs.x.dx;
             }
             return(result);
        }

        EMSAllocatePages(handle, pages)   /*размещает обработчик с 'pages'
                                                     логических страниц*/
        unsigned int *handle, pages;
        {
             inregs.h.ah = 0x43;      /*функция "Разместить страницы"
                                        спецификации расширенной памяти*/

             inregs.x.bx = pages;     /*кол-во логических страниц для
                                               размещения*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             if (!result)             /*функция завершилась успешно*/
                 *handle = outregs.x.dx;    /*обработчик   менеджера
                                          расширенной памяти для работы с
                                            этими страницами*/
             return(result);
        }

        EMSMapHandlePage(handle, page, frame)   /*отображает логическую
                                        страницу  в 'кадр'*/
        unsigned int handle, page, frame;
        {
             inregs.h.ah = 0x44;      /*функция "Отобразить/перестать отоб-
                          ражать страницы спецификации расширенной памяти*/
             inregs.h.al = frame & 0x00ff;  /*кадр целевой страницы*/
             inregs.x.bx = page;     /*номер логической страницы, в которую
                                               отображать*/
             inregs.x.dx = handle;   /*обработчик, которому принадлежит
                                            логическая страница*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             return(result);
        }

        EMSDeallocatePages(handle)   /*освобождает обработчик и все его
                                                      страницы*/
        unsigned int handle;
        {
             inregs.h.ah = 0x45;      /*функция "Освободить страницы"
                                        спецификации расширенной памяти*/
             inregs.x.dx = handle;    /*обработчик, назначенный для осво-
                                   бождения менеджеру расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;

                                      - 7-60 -
             return(result);
        }

        EMSGetVersion(emsver)   /*возвращает номер версии программного
                               обеспечения менеджера расширенной памяти*/
        char *emsver;
        {
             inregs.h.ah = 0x46;      /*функция "Получить версию"
                                        спецификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs);
             if (!(result & 0xFF00)) {    /*функция завершилась успешно*/
                 emsver[0] = ((result & 0x00F0) >> 4) + '0';
                 emsver[1] = '.';
                 emsver[2] = (result & 0x000F) + '0';
                 emsver[3] = '\0';
             }
             return(result >> 8);
        }

        EMSSavePageMap(handle)   /*сохраняет контекст менеджера расширенной
                                  памяти в области сохранения контекста
                                   менеджера расширенной памяти*/
        unsigned int handle;
        {
             inregs.h.ah = 0x47;  /*функция "Сохранить отображение страниц"
                                          спецификации расширенной памяти*/
             inregs.x.dx = handle;    /*обработчик, для которого выполняет-
                                               ся сохранение*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             return(result);
        }

        EMSRestorePageMap(handle)   /*восстанавливает контекст менеджера
                                     расширенной памяти из области сохране-
                               ния контекста менеджера расширенной памяти*/
        unsigned int handle;
        {
             inregs.h.ah = 0x48;  /*функция "Восстановить отображение стра-
                                     ниц" спецификации расширенной памяти*/
             inregs.x.dx = handle;    /*область контекста, откуда выполня-
                                               ется восстановление*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             return(result);
        }

        EMSGetHandleCnt(hcnt)   /*возвращает кол-во открытых обработчиков
                                                     (1 - 255*/
        unsigned int *hсnt;
        {
             inregs.h.ah = 0x4B;      /*функция "Получить кол-во обработ-
                                  чиков спецификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             if (!result)    {        /*функция завершилась успешно*/
                 *hcnt = outregs.x.bx;
             }
             return(result);
        }

                                      - 7-61 -

        EMSGetHandlePages(handle, pages)   /*возвращает кол-во страниц,
                                            размещенных для обработчика*/
        unsigned int handle, *pages;
        {
             inregs.h.ah = 0x4С;      /*функция "Получить страницы обработ-
                                    чика" спецификации расширенной памяти*/
             inregs.x.dx = handle;    /*обработчик, которому, полагается,
                                               принадлежат страницы*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>>8;
             if (!result)             /*функция завершилась успешно*/
                 *pages = outregs.x.bx;
             return(result);
        }

        EMSGetAllHandlePages(hp, hpcnt)     /*возвращает кол-во страниц,
                                            размещенных всем обработчикам*/
        HANDLE_PAGE *hp;
        unsigned int *hpcnt;
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.h.ah = 0x4D;      /*функция "Получить страницы  всех
                            обработчиков" спецификации расширенной памяти*/
             segregs.es = segregs.ds;     /*сегмент массива HANDLE_PAGE*/
             inregs.x.di = (unsigned int) hp;   /*смещение массива HANDLE_
                                                               PAGE*/
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                             &segregs) >> 8;
             if (!result)             /*функция завершилась успешно*/
                 *hpcnt = outregs.x.bx;
             return(result);
        }

        EMSGetPageMap(map)   /*получает контекст менеджера расширенной па-
                        мяти в область сохранения контекста пользователя*/
        PMAP *map;
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             segregs.es = segregs.ds;     /*использовать es = ds*/
             inregs.x.ax = 0x4E00;    /*функция "Получить отображение стра-
                                     ниц" спецификации расширенной памяти*/
             inregs.x.di = (unsigned int) map;  /*указатель на массив отоб-
                                                            ражения*/
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                       &segregs) >> 8;
             return(result);
        }

        EMSSetPageMap(map)   /*устанавливает контекст менеджера расширенной
                      памяти из области сохранения контекста пользователя*/
        PMAP *map;
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax = 0x4E01;  /*функция "Установить отображение стра-
                                     ниц" спецификации расширенной памяти*/
             inregs.x.si = (unsigned int) map;  /*указатель на массив отоб-
                                                            ражения*/

                                      - 7-62 -
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                       &segregs) >> 8;
             return(result);
        }

        EMSGetSetPageMap(srcmap, destmap)    /*сохраняет контекст менеджера
                           сширенной памяти в destmap и затем устанавливает
                          контекст менеджера расширенной памяти из srcmap*/
        PMAP *srcmap, *destmap;
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             segregs.es = segregs.ds;     /*оба отображения в ds*/
             inregs.x.ax = 0x4E02;  /*функция "Получить и установить отоб-
                         ражение страниц" спецификации расширенной памяти*/
             inregs.x.si = (unsigned int) srcmap;  /*указатель на массив
                                                  отображения-источника*/
             inregs.x.di = (unsigned int) destmap;  /*указатель на массив
                                                  отображения-приемника*/
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                       &segregs) >> 8;
             return(result);
        }

        EMSGetPageMapeSize(size)   /*получает размер области сохранения
                                            контекста пользователя*/
        unsigned int *size;
        {
             inregs.x.ax = 0x4E03;    /*функция "Получить размер отображе-
                            ния страниц" спецификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs);
             if (!(result) & 0xFF00))      /*функция завершилась успешно*/
                 *size = outregs.h.al;
             return(result >> 8);
        }

        EMSGetPPageMap(pmap, savearea)   /*получает частичный контекст ме-
                                  неджера расширенной памяти в область со-
                                        хранения пользователя*/
        PPMAP *pmap;
        PCONTEXT *savearea;
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             segregs.es = segregs.ds;     /*использует es = ds*/
             inregs.x.ax = 0x4F00;  /*функция "Получить частичное отобра-
                           жение страниц" спецификации расширенной памяти*/
             inregs.x.si = (unsigned int) pmap;  /*какие кадры мы хотим*/
             inregs.x.di = (unsigned int) savearea; /*указатель на массив
                                                              отображения*/
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                        &segregs) >> 8;
             return(result);
        }

        EMSSetPPageMap(savearea)    /*устанавливает частичный контекст ме-
                                  неджера расширенной памяти из области
                                        сохранения пользователя*/
        PCONTEXT *savearea;

                                      - 7-63 -
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax = 0x4F01;  /*функция "Установить частичное отобра-
                           жение страниц" спецификации расширенной памяти*/
             inregs.x.si = (unsigned int) savearea; /*кадры, которые мы хо-
                                                      тим восстановить*/
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                       &segregs) >> 8;
             return(result);
        }

        EMSGetPPageMapeSize(count,size)   /*получает размер области, необ-
                                             ходимой для сохранения*/
        unsigned int count,*size;
        {
             inregs.x.ax =0x4F02;    /*функция "Получить размер частично-
                                          го отображения страниц" специфи-
                                                 кации расширенной памяти*/
             inregs.x.bx = count;    /*кол-во кадров для сохранения*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs);
             if (!(result) & 0xFF00))      /*функция завершилась успешно*/
                 *size = outregs.h.al;
             return(result >> 8);
        }

        EMSMapMultPages(handle, map, method, count)   /*отображает count
                                             страниц в map для handle*/
        unsigned int handle;     /*обработчик, для которого отображаются
                                                            страницы*/
        LOG_TO_PHYS *map;        /*отображение логических страниц в физи-
                                                             ческие*/
        unsigned int method;     /*используются номера кадра страниц или
                                      адреса отображаемых сегментов*/
        unsigned int count;      /*кол-во элементов в отображении*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.h.ah =0x50;      /*функция "Отобразить много страниц
                             обработчика" спецификации расширенной памяти*/
             inregs.h.al = (unsigned char) method;
             inregs.x.cx = count;    /*кол-во страниц для отображения*/
             inregs.x.dx = handle;   /*обработчик, которому эти страницы
                                                     принадлежат*/
             inregs.x.si = (unsigned int) map; /*страницы для отображения*/
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                       &segregs) >> 8;
             return(result);
        }

        EMSReallocPages(handle, pages)   /*изменяет размещение handle для
                                             pages*/
        unsigned int handle, *pages;
        {
             inregs.h.ah =0x51;      /*функция "Переразместить страницы"
                                          спецификации расширенной памяти*/
             inregs.x.bx = *pages;   /*кол-во логических страниц, которое
                                                   будет после выполнения*/
             inregs.x.dx = handle;   /*обработчик, для которого переразме-

                                      - 7-64 -
                                                   щается страница*/
           result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >> 8;
             if (!result)                   /*функция завершилась успешно*/
                 *pages = outregs.x.bx;     /*новое кол-во страниц*/
             return(result);
        }

        EMSGetHandleAttr(handle, attr)   /*получает атрибут обработчика*/
        unsigned int handle, *attr;
        {
             inregs.x.ax =0x5200;      /*функция "Получить атрибут обра-
                                  ботчика спецификации расширенной памяти*/
             inregs.x.dx = handle;
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs);
             if (!(result & 0xFF00))        /*функция завершилась успешно*/
                 *attr = outregs.h.al;      /*атрибут*/
             return(result >> 8);
        }

        EMSSetHandleAttr(handle, attr)   /*устанавливает атрибут обработ-
                                                                 чика*/
        unsigned int handle, attr;
        {
             inregs.x.ax =0x5201;      /*функция "Установить атрибут об-
                                работчика спецификации расширенной памяти*/
             inregs.x.dx = handle;
             inregs.h.bl = attr & 0x00FF;
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs)>> 8;
             return(result);
        }
        EMSGetAttrCap(cap)               /*получить возможности атрибута*/
        unsigned int *cap;
        {
             inregs.x.ax =0x5202;      /*функция "Получить возможности ат-
                                  рибута спецификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs);
             if (!(result & 0xFF00))     /*успех */
                   *cap = outregs.h.al;
             return(result >> 8);
        }
        EMSGetHandleName(handle, name)  /*получает имя обработчика handle*/
        unsigned int handle;    /*обработчик, для которого получается имя*/
        char *name;               /*буфер для получения имени обработчика*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5300;    /*функция "Получить имя обработчика"*/
             segregs.es = segregs.ds;
             inregs.x.di = (unsigned int) name;
             inregs.x.dx = handle;
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                       &segregs) >> 8;
             return(result);
        }

        EMSSetHandleName(handle, name)  /*устанавливает имя обработчика
                                                                  handle*/
        unsigned int handle;     /*обработчик, для которого устанавл. имя*/

                                      - 7-65 -
        char *name;               /*буфер с именем обработчика*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5301;    /*функция "Установить имя обработч."*/
             inregs.x.si = (unsigned int) name;
             inregs.x.dx = handle;
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                       &segregs) >> 8;
             return(result);
        }


        EMSGetHandleDir(hnt, hn_cnt)  /*получает имя каталога обработчика
                                                                  handle*/
        HANDLE_NAMES *hnt;       /*указатель на таблицу имен обработчиков*/
        unsigned int *hn_cnt;    /*возвращенное кол-во элементов*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5400;    /*функция "Получить каталог обработчи-
                                      ка" спецификации расширенной памяти*/
             inregs.x.di = (unsigned int) hnt;
             segregs.es = segregs.ds;
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                            &segregs);
             if (!(result & 0xFF00))        /*функция завершилась успешно*/
                *hn_cnt = outregs.h.al;     /*возврат кол-ва полученных
                                                        имен обработчиков*/
             return(result >> 8);
        }

        EMSSearhHandleName(name, handle)  /*поиск названного  обработчика*/
        char *name;                       /*имя, которое нужно искать*/
        unsigned int *handle;            /*возвращаемый номер обработчика*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5401;    /*функция "Поиск названного обработчи-
                                      ка" спецификации расширенной памяти*/
             inregs.x.si = (unsigned int) name;
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                            &segregs) >> 8;
             if (!result)                   /*функция завершилась успешно*/
                *handle = outregs.x.dx;    /*возврат значения обработчика*/
             return(result);
        }

        EMSGetTotalHandles(handle_count)  /*Получить общее кол-во  обработ-
                                                                    чиков*/
        unsigned int *handle_count;
        {
             inregs.x.ax =0x5402;       /*подфункция "Получить общее кол-во
                            обработчиков" спецификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             if (!result)
                *handle_count = outregs.x.bx;
             return(result);
        }


                                      - 7-66 -
        EMSMoveRegion(rp)                          /*передвинуть область*/
        MOVE_XCHG *rp;                 /*указатель на дескриптор области*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5700;           /*функция "Передвинуть область"
                                          спецификации расширенной памяти*/
             inregs.x.si = (unsigned int) rp;
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                            &segregs) >> 8;
             return(result);
        }

        EMSExchangeRegion(rp)                         /*обменять область*/
        MOVE_XCHG *rp;                 /*указатель на дескриптор области*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5701;              /*функция "Обменять область"
                                          спецификации расширенной памяти*/
             inregs.x.si = (unsigned int) rp;
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                            &segregs) >> 8;
             return(result);
        }

        EMSGetMapAddrArray(mpaa, mpa_cnt)   /*получить массив отображаемых
                                               физических адресов*/
        MAP_PHYS_PAGE *mpaa;           /*указатель на массив отображаемых
                                               физических адресов*/
        unsigned int *mpa_cnt;         /*кол-во возвращенных элементов*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5800;              /*функция "Получить массив
                                         отображаемых физических адресов"*/
             inregs.x.di = (unsigned int) mpaa;
             segregs.es = segregs.ds;
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                            &segregs) >> 8;
             if (!result) >> 8);            /*функция завершилась успешно*/
                *mpa_cnt = outregs.x.cx;    /*возврат кол-ва отображаемых
                                                  физических страниц*/
             return(result);
        }

        EMSGetMapAddrCount(mpa_cnt)   /*получить кол-во отображаемых
                                               физических адресов*/
        unsigned int *mpa_cnt;         /*кол-во отображаемых физических
                                                             страниц*/
        {
             inregs.x.ax =0x5801;              /*функция "Получить кол-во
                                         отображаемых физических адресов"*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             if (!result)                   /*функция завершилась успешно*/
                *mpa_cnt = outregs.x.cx;    /*возврат кол-ва отображаемых
                                                  физических страниц*/
             return(result);
        }


                                      - 7-67 -
        EMSGetHardwareInfo(hwp)            /*получить информацию об обору-
                                 довании спецификации расширенной памяти*/
        HARDWARE_INFO *hwp;            /*указатель на область для получе-
                                        ния информации об оборудовании*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5900;      /*функция "Получить информацию  об
                             оборудовании спецификации расширенной памяти*/
             inregs.x.di = (unsigned int) hwp;
             segregs.es = segregs.ds;
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                            &segregs) >> 8;
             return(result);
        }

        EMSGetRawPageCount(rpg_cnt, urpg_cnt)  /*получить кол-во исходных
                                                                  страниц*/
        unsigned int *rpg_cnt;                  /*кол-во исходных страниц*/
        unsigned int *urpg_cnt;   /*кол-во неразмещенных исходных страниц*/
        {
             inregs.x.ax =0x5901;      /*функция "Получить кол-во исходных
                                  страниц спецификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             if (!result)   {               /*функция завершилась успешно*/
                *rpg_cnt = outregs.x.dx;  /*общее кол-во исходных страниц*/
                *urpg_cnt = outregs.x.bx; /*кол-во неразмещенных исходных
                                                            страниц*/
             }
             return(result);
        }

        EMSAllocateStdPages(handle, pages)  /*размещает обработчик с 'pag-
                                             es' стандартных страниц*/
        unsigned int handle, *pages;
        {
             inregs.x.ax =0x5A00;    /*функция "Разместить стандартные
                               страницы" спецификации расширенной памяти*/
             inregs.x.bx = pages;    /*кол-во логических страниц для раз-
                                                         мещения*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >> 8;
             if (!result)                  /*функция завершилась успешно*/
                *handle = outregs.x.dx;  /*обработчик менеджера расширенной
                                 памяти для применения с этими страницами*/
             return(result);
        }

        EMSAllocateRawPages(handle, pages)  /*размещает обработчик с 'pag-
                                                     es' исходных страниц*/
        unsigned int handle, *pages;
        {
             inregs.x.ax =0x5A01;    /*функция "Разместить исходные стра-
                                   ницы" спецификации расширенной памяти*/
             inregs.x.bx = pages;    /*кол-во логических страниц для раз-
                                                         мещения*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >> 8;
             if (!result)                  /*функция завершилась успешно*/
                *handle = outregs.x.dx;  /*обработчик менеджера расширенной

                                      - 7-68 -
                                 памяти для применения с этими страницами*/
             return(result);
        }

        EMSGetAltMapRegSet(set,pmap)  /*получает альтернативный набор ре-
                      гистров отображения спецификации расширенной памяти*/
        unsigned int *set;            /*текущий альтернативный набор ре-
                                                   гистров отображения*/
        PMAP far **pmap;              /*указатель на указатель области со-
                                                       хранения контекста*/
        {
             inregs.x.ax =0x5B00;    /*функция "Получить альтернативный на-
                                               бор регистров отображения"*/
             segread(&segregs);

             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                        &segregs) >> 8;
             if (!result)    {
                *set = outregs.h.bi;             /*текущий активный набор*/
                if (*set == 0)   {     /*фальшивый альтернативный набор ре-
                                                         гистров*/
                   FP_OFF(*pmap) = outregs.x.di;   /*смещение области кон-
                                         текста операционной среды (OS)*/
                   FP_SEG(*pmap) = segregs.es;     /*сегмент  области кон-
                                         текста операционной среды (OS)*/
                }
             }
             return(result);
        }

        EMSSetAltMapRegSet(set,pmap)  /*устанавливает альтернативный набор
                    регистров отображения спецификации расширенной памяти*/
        unsigned int set;             /*новый альтернативный набор ре-
                                                   гистров отображения*/
        PMAP *pmap;              /*указатель области сохранения контекста*/
        {
             segread(&segregs);           /*заполнить  регистры сегментов*/
             inregs.x.ax =0x5B01;  /*функция "Установить альтернативный на-
                                               бор регистров отображения"*/
             inregs.h.bl = set & 0x00FF;
                if (set == 0)   {     /*фальшивый альтернативный набор ре-
                                                         гистров*/
                   inregs.x.di = (unsigned int) pmap;
                   segregs.es = segregs.ds;
                }
             result = (unsigned int) int86x(EMM_INT, &inregs, &outregs,
                        &segregs) >> 8;
             if (!result)
       }

        EMSGetAltMapArraySize(size)  /*получает размер массива сохранения
                                              альтернативного отображения*/
        unsigned int size;             /*кол-во отображаемых физических
                                                   страниц*/
        {
             inregs.x.ax =0x5B02;  /*функция "Получить размер массива со-
                                    хранения альтернативного отображения"*/

                                      - 7-69 -
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             if (!result)                  /*успех*/
                *size = outregs.x.dx;             /*размер массива*/
             return(result);
        }

        EMSAllocAltMapRegSet(set)  /*размещает альтернативный набор регис-
                                                      тров отображения*/
        unsigned int *set;             /*номер, размещенного набора*/
        {
             inregs.x.ax =0x5B03;  /*функция "Разместить альтернативный
                                    набор регистров отображения"*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             if (!result)                  /*успех*/
                *set = outregs.h.bl;          /*номер размещенного набора*/
             return(result);
        }

        EMSDeallocAltMapRegSet(set)  /*освобождает альтернативный набор ре-
                                                      гистров отображения*/
        unsigned int set;             /*номер, освобождаемого набора*/
        {
             inregs.x.ax =0x5B04;  /*функция "Освободить альтернативный
                                            набор регистров отображения"*/
             inregs.h.bl = set & 0x00FF;
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             return(result);
        }
        EMSAllocDMARegSet(set)           /*размещает набор регистров ПДП*/
        unsigned int *set;                  /*номер, размещаемого набора*/
        {
             inregs.x.ax =0x5B05;  /*функция "Разместить  набор  регистров
                                    ПДП" спецификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             if (!result)                  /*успех*/
                *set = outregs.h.bl;          /*номер размещенного набора*/
             return(result);
        }

        EMSEnableDMARegSet(set, channel) /*разрешает набор регистров ПДП*/
        unsigned int set;                 /*номер, разрешаемого набора*/
        unsigned int channel;             /*номер канала ПДП для связыва-*/
                                             ния с регистром отображения*/
        {
             inregs.x.ax =0x5B06;       /*функция "Разрешить  набор  регис-
                                тров ПДП" спецификации расширенной памяти*/
             inregs.h.bl = set & 0x00FF;
             inregs.h.dl = channel & 0x00FF;
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             return(result);
        }

        EMSDisableDMARegSet(set)         /*запрещает набор регистров ПДП*/
        unsigned int set;                 /*номер запрещаемого набора*/
        {
             inregs.x.ax =0x5B07;    /*функция "Запретить  набор  регистров
                                     ПДП" спецификации расширенной памяти*/

                                      - 7-70 -
             inregs.h.bl = set & 0x00FF;
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             return(result);
        }

        EMSDDeallocDMARegSet(set)    /*освобождает набор регистров ПДП*/
        unsigned int set;                 /*номер освобождаемого набора*/
        {
             inregs.x.ax =0x5B08;   /*функция "Освободить  набор  регистров
                                     ПДП" спецификации расширенной памяти*/
             inregs.h.bl = set & 0x00FF;
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             return(result);
        }

        EMSPrepareForWarmboot()     /*подготавливает оборудование специфи-
                      кации расширенной памяти к программному перезапуску*/
        {
             inregs.h.ah =0x5C;     /*функция "Подготовиться к программному
                             перезапуску" спецификации расширенной памяти*/
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             return(result);
        }

        EMSEnableOSFunc(key)         /*разрешить набор функций операционной
                                                                    среды*/
        long *key;      /*ключ доступа к операционной среде - должен быть
                                    равен 0, когда применяется первый раз*/
        {
             inregs.x.ax =0x5D00;   /*функция "Разрешить набор функций опе-
                         рационной среды" спецификации расширенной памяти*/
             if (*key !=0)   {
                 inregs.x.bx = FP_OFF(*key);
                 inregs.x.cx = FP_SEG(*key);
        }
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
                if (!result)   {
                   FP_OFF(*key) = outregs.x.bx;
                   FP_SEG(*key) = outregs.x.cx;
                }
             }
             return(result);
        }

        EMSDisableOSFunc(key)         /*запретить набор функций операционной
                                                                    среды*/
        long *key;      /*ключ доступа к операционной среде - должен быть
                                    равен 0, когда применяется первый раз*/
        {
             inregs.x.ax =0x5D01;   /*функция "Запретить набор функций опе-
                         рационной среды" спецификации расширенной памяти*/
             if (*key !=0)   {
                 inregs.x.bx = FP_OFF(*key);
                 inregs.x.cx = FP_SEG(*key);
        }
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;


                                      - 7-71 -
                if (!result)   {
                   FP_OFF(*key) = outregs.x.bx;
                   FP_SEG(*key) = outregs.x.cx;
                }
             }
             return(result);
        }

        EMSReturnAccessKey(key)    /*возвращает ключ доступа к операционной
                                       среде менеджеру расширенной памяти*/
        long *key;                    /*ключ доступа к операционной среде*/
        {
             inregs.x.ax =0x5D02;      /*функция "Возвратить ключ доступа к
                      операционной среде" спецификации расширенной памяти*/
                 inregs.x.bx = FP_OFF(key);
                 inregs.x.cx = FP_SEG(key);
             result = (unsigned int) int86(EMM_INT, &inregs, &outregs) >>8;
             return(result);
        }
        ------------------------------------------------------------------

                              Листинг 7-6. EMMFUNC.H
        ------------------------------------------------------------------
        extern void ShowEMMErr(unsigned int errcode,unsigned int lineno,
                   char *filename);
        extern int EMSGetStatus(void);
        extern int EMSGetFrameAddr(char far * *pfa);
        extern int EMSGetPageCnt(unsigned int *una,unsigned int *tot);
        extern int EMSAllocatePages(unsigned int *handle,unsigned int pages);
        extern int EMSMapHandlePage(unsigned int handle,unsigned int page,
                   unsigned int frame);
        extern int EMSDeallocatePages(unsigned int handle);
        extern int EMSGetVersion(char *emsver);
        extern int EMSSavePageMap(unsigned int handle);
        extern int EMSRestorePageMap(unsigned int handle);
        extern int EMSGetHandleCnt(unsigned int *hcnt);
        extern int EMSGetHandlePages(unsigned int handle,unsigned int
                   *pages);

        extern int EMSGetAllHandlePages(struct handle_page *hp,unsigned int
                   *hpcnt);
        extern int EMSGetPageMap(unsigned int *map);
        extern int EMSSetPageMap(unsigned int *map);
        extern int EMSGetSetPageMap(unsigned int *srcmap,unsigned int
                   *destmap);
        extern int EMSGetPPageMapSize(unsigned int *size);
        extern int EMSGetPPageMap(struct ppmap *pmap,unsigned char
                   *savearea);
        extern int EMSSetPPageMap(unsigned char *savearea);
        extern int EMSGetPPageMapSize(unsigned int count,unsigned int
                   *size);
        extern int EMSMapMultPages(unsigned int handle,struct log_to_phys
                   *map,unsigned int method,unsigned int count);
        extern int EMSReallocaPages(unsigned int handle,unsigned int
                   *pages);
        extern int EMSGetHandleAttr(unsigned int handle,unsigned int
                   *attr);

                                      - 7-72 -
        extern int EMSSetHandleAttr(unsigned int handle,unsigned int
                    attr);
        extern int EMSGetAttrCap(unsigned int *cap);
        extern int EMSGetHandleName(unsigned int handle,char *name);
        extern int EMSSetHandleName(unsigned int handle,char *name);
        extern int EMSGetHandleDir(struct handle_names *hnt,unsigned int
                   *hn_cnt);
        extern int EMSSearchHandleName(char *name,unsigned int *handle);
        extern int EMSGetTotalHandles(unsigned int *handle_count);
        extern int EMSMoveRegion(struct move_xchg *rp);
        extern int EMSExchangeRegion(struct move_xchg *rp);
        extern int EMSGetMapAddrArray(struct map_phys_page *mpaa,unsigned
                   int *mpa_cnt);
        extern int EMSGetMapAddrCount(unsigned int *mpa_cnt);
        extern int EMSGetHardwareInfo(struct hardware_info *hwp);
        extern int EMSGetRawPageCount(unsigned int *rpg_cnt,unsigned int
                   *urpg_cnt);
        extern int EMSAllocateStdPages(unsigned int *handle,unsigned int
                   pages);
        extern int EMSAllocateRawPages(unsigned int *handle,unsigned int
                   pages);
        extern int EMSGetAltMapRegSet(unsigned int *set,unsigned char far
                   * *pmap);
        extern int EMSSetAltMapRegSet(unsigned int set,unsigned char
                   *pmap);
        extern int EMSGetAltMapArraySize(unsigned int *size);
        extern int EMSAllocAltMapRegSet(unsigned int *set);
        extern int EMSDeallocAltMapRegSet(unsigned int set);
        extern int EMSAllocDMARegSet(unsigned int *set);
        extern int EMSEnableDMARegSet(unsigned int set,unsigned int
                   channel);
        extern int EMSDisableDMARegSet(unsigned int set);
        extern int EMSDeallocDMARegSet(unsigned int set);
        extern int EMSPrepareForWarmboot(void);
        extern int EMSEnableOSFunc(long *key);
        extern int EMSDisableOSFunc(long *key);
        extern int EMSReturnAccessKey(long key);
        -------------------------------------------------------------------


                    Листинг 7-7. EMMEXIST.C
        ------------------------------------------------------------------
        #include 
        #include 
        #include 
        #include 
        #include "emmconst"

        #define DOS_INT           0x21     /*диспетчер функций DOS*/
        #define DOS_IOCTL         0x44     /*функция IOCTL DOS*/
        #define IOCTL_GETINFO     0x00     /*подфункция "Получить информа-
                                                 цию об устройстве" IOCTL*/
        #define IOCTL_OUTSTAT     0x07     /*подфункция "Получить состояние
                                                        вывода" IOCTL*/
        #define READY_OUTPUT      0xFF     /*устройство готово к выводу*/
        #define IS_DEVICE         0x0080   /*обработчик принадлежит устрой-
                                                                    ству*/

                                      - 7-73 -
        static char device_name[9] = EMM_DEVICE;

             /*
             Контролируется наличие расширенной памяти с помощью метода
             "открытого обработчика". Устанавливает emm_present в '1',
             если расширенная память присутствует, в '0' - если нет.
             Функция возвращает '0', если тест наличия завершился успеш-
             но. В противном случае возвращается код ошибки DOS вызова
             функции DOS, которая не выполнилась во время теста наличия
             расширенной памяти.
             */

        emm_exists(emm_present)
        int *emm_present;               /*указатель на индикатор наличия
                                             менеджера расширенной памяти*/
        {
             int return_code;            /*код возврата операции с файлом*/
             int handle;                 /*обработчик файлов*/
             unsigned int dev_attr;       /*атрибуты драйвера устройства*/
             unsigned int dev_status;     /*выходное состояние устройства*/

             if (_dos_open(device_name, O_RDONLY, &handle))  {
                                                /*не удалось открыть файл*/
                if (errno == ENOENT)  {              /*файл не существует*/
                   return_code = 0;             /*мы ожидали, что это могло
                                                               случиться*/
                   *emm_present = 0;          /*менеджер расширенной памяти
                                                  определенно отсутствует*/
                } else
                   return_code = errno;       /*тест наличия завершился
                                                               безуспешно*/
             } else
                if (!(return_code = ioctl_getattr(handle, &dev_attr))) {
                                                       /*получен атрибут*/
                   if (!(return_code = ioctl_outstat(handle, &dev_status)))
                                            /*получено выходное состояние*/
                                  /*менеджер расширенной памяти присутству-
                                    ет, если обработчик принадлежит устрой-
                                             ству и готов к выводу*/
                *emm_present = ((dev_status == READY_OUTPUT) && (dev_attr
                                           & IS_DEVICE)) ? 1 : 0;
                close(handle);             /*закрывается обработчик файла*/
             }
             return(return_code);
        }

             /*
             Получается слово атрибутов DOS для открытого обpаботчика,
             связанного с открытым файлом или устройством. Возвращает 0,
             если функция завершилась успешно, в противном случае воз-
             вращает код ошибки DOS
             */

        ioctl_getattr(handle, attrib)
        int handle;                /*открытый обpаботчик файла/устройства*/
        unsigned int *attrib; /* -> возвращенная информация об устройстве*/
        {

                                      - 7-74 -
             int rc;
             union REGS regs;
             regs.h.ah = DOS_IOCTL;  /*управление вводом/выводом  DOS  для
                                                                устройств*/
             regs.h.al = IOCTL_GETINFO;  /*получить информацию об устройс-
                                                                     тве*/
             regs.x.bx = handle;
             int86(DOS_INT, &regs, &regs);            /*вызов функции DOS*/
             if (!regs.x.cflag)  {             /*если не произошло ошибки*/
                *attrib = regs.x.dx;               /*возвращаются атрибуты
                                                         файла/устройства*/
                rc = 0;                     /*функция выполнилась успешно*/
             } else
                rc = regs.x.ax;             /*возвращается код ошибки*/
             return(rc);
        }

             /*
             Получает выходное состояние файла или устройства. Возвра-
             щенное состояние 0 означает, что устройство неготово для
             вывода; состояние 0xFF00 означает, что устройство готово
             для вывода. Возвращается 0, если функция завершилась ус-
             пешно. В противном случае возвращается код ошибки DOS.
             */

        ioctl_outstat(handle, status)
        int handle;                /*открытый обpаботчик файла/устройства*/
        unsigned int *status;                /* -> слово состояния вывода*/
        {
             int rc;
             union REGS regs;
             regs.h.ah = DOS_IOCTL;  /*управление вводом/выводом  DOS  для
                                                                устройств*/
             regs.h.al = IOCTL_OUTSTAT;       /*получить состояние вывода*/
             regs.x.bx = handle;
             int86(DOS_INT, &regs, &regs);            /*вызов функции DOS*/
             if (!regs.x.cflag)  {             /*если не произошло ошибки*/
                *status = regs.h.al;               /*возвращается состояние
                                                                   вывода*/
                rc = 0;                     /*функция выполнилась успешно*/
             } else
                rc = regs.x.ax;             /*возвращается код ошибки*/
             return(rc);
        }

             /*
             Проверяет наличие pасшиpенной памяти с помощью метода
             "получить вектор". Устанавливает emm_present в '1', если
             расширенная память присутствует, '0' - если нет. Данная
             функция всегда возвращает '0'.
             */

        emm_exists2(emm_present)
        int *emm_present;
        {
             int len;
             char far *device_name;        /*указатель на имя символьного

                                      - 7-75 -
                                                               устройства*/
             char *np;
             unsigned int get_int_seg();

             FP_SEG(dev_name) = get_int_seg(EMM_INT);   /*сегмент драйвера
                                  устройства менеджеpа pасшиpенной памяти*/

             FP_OFF(dev_name) = 10;      /*смещение имени драйвера символь-
                                                          ного устройства*/

             /*увидеть, находится ли имя менеджеpа pасшиpенной памяти
                       по смещению 10 в сегменте EMM_INT*/

             for (len  = 8, np = device_name; len && *dev_name++ == *np++;
                                                               len--);

             *emm_present = (len) ? 0 : 1;       /*если все символы совпа-
                              ли менеджеp pасшиpенной памяти присутствует*/
             return(0);                      /*всегда завершается успешно*/
        }

             /*
             Возвращает адрес сегмента вектора прерываний "intno"
             */

        unsigned int get_int_seg(intno)
        int intno;
        {
             union REGS regs;
             struct SREGS segregs;

             regs.h.al = (unsigned char) intno;
             regs.h.ah = 0x35;            /*функция "Получить вектор" DOS*/
             intdosx(&regs, &regs, &segregs);
             return((unsigned) segregs.es);
        }
        -------------------------------------------------------------------

                    Листинг 7-8. SNAPSHOT.C
        -------------------------------------------------------------------
             /*
                Наименование: SNAPSHOT.C
                Назначение:   утилита, остающаяся резидентно в памяти после
                              завершения, для сохранения образов текстовых
                              экранов в pасшиpенной памяти.
             */

        #include 
        #include 
        #include 
        #include 
        #include 

        #include "emmconst.h"    /*константы менеджеpа pасшиpенной памяти*/
        #include "emmtypes.h" /*структуры данных менеджеpа pасшиpенной па-
                                                                     мяти*/
        #include "emmerr.h"    /*коды ошибок менеджеpа pасшиpенной памяти*/

                                      - 7-76 -
        #include "emmfunc.h"  /*объявления  функций  менеджеpа pасшиpенной
                                                                   памяти*/

        #define PRTSC_INT          5           /*прерывание печати экрана*/
        #define HANDLE_NAME        "SNAPSHOT"  /*имя обpаботчика pасшиpен-
                                                               ной памяти*/
        #define MAX_SCR            500         /*максимальное кол-во сохра-
                                                           няемых экранов*/
        #define MDA_SEG            0xB000      /*сегмент буфера адаптера
                                                     монохромного дисплея*/
        #define CGA_SEG            0xB800      /*сегмент буфера адаптера
                                            цветного графического дисплея*/
        #define SCR_ROWS           25          /*предполагается 25 строк -
                                              процедура должна определить*/
                                               /*фактическое кол-во строк
                                                 зависит от адаптера*/

        #pragma pack(1)         /*структура данных, выровненная по байтам*/

        #define DisplayError(rc)      ShowEMMErr(rc, __LINE__, __FILE__)

        typedef struct bios_video_data  {     /*базисные видеоданные BIOS*/
             unsigned char   crt_mode;         /*режим дисплея*/
             unsigned int    crt_cols;         /*кол-во колонок на экране*/
             unsigned int    crt_len;          /*длина буфера регенерации в
                                                                   байтах*/
             unsigned int    crt_start;        /*начальный адрес в буфере
                                                           регенерации*/
             unsigned int    cursor_pos[8];    /*положение курсора для 8
                                                           страниц*/
             unsigned int    cursor_mode;      /*текущая установка режима
                                                                курсора*/
             unsigned char   active_page;      /*текущая отображаемая стра-
                                                                    ница*/
             unsigned int    addr_6845;        /*адрес базы активной платы
                                                            отображения*/
             unsigned char   crt_mode_set;     /*текущая установка регистра
                                                                     3х8*/
             unsigned char   crt_palette;      /*текущая установка палитры
                                                - цветная плата*/
        } BIOS_VIDEO_DATA;

        typedef struct scr  {                  /*дескриптор данных экрана*/
             unsigned int scr_page;            /*начальная страница pасши-
                                                pенной памяти для экранов*/
             unsigned int    scr_offset;       /*начальное смещение расши-
                                                ренной памяти для экранов*/
             unsigned int    scr_width;        /*кол-во колонок на экране*/
             unsigned int    scr_len;          /*длина экрана в байтах*/
        } SCR;

        typedef struct scr_index {           /*индексная структура экрана*/
             void (interrupt far *scr_int5)(); /*указатель на нашу програм-
                                               му обслуживания прерывания*/
             unsigned int    scr_count;        /*кол-во экранов, сохранен-
                                                ных в текущий момент*/
             unsigned int scr_max;            /*максимальное кол-во экранов

                                      - 7-77 -
                                                         для сохранения*/
        } SCR_INDEX;

             /*
                     глобальные данные
             */

        void (interrupt far *old_int5)();   /*старый вектор печати экрана*/
        PMAP *emm_save;                     /*указатель на область сохране-
                              ния контекста менеджеpа pасшиpенной памяти*/
        unsigned int emm_tpages,            /*общее кол-во страниц pасши-
                                                            pенной памяти*/
                         emm_apages,        /*доступное кол-во страниц
                                                      pасшиpенной памяти*/
                         emm_handle,        /*обpаботчик pасшиpенной памя-
                                                                       ти*/
                         emm_pages,         /*кол-во страниц, принадлежащих
                                                              обpаботчику*/
                         isr_status,        /*0: isr следует сцепить,
                                            <>0: isr следует обслужить*/
                         terminate_flag;    /*1: закончить эту программу*/
        char   far       *page_frame;     /*удаленная -> в кадровый буфер*/
        SCR_INDEX        far *ip;          /*удаленная -> в индекс экрана*/
        SCR              far *sp;      /*удаленная -> в дескриптор экрана*/

        BIOS_VIDEO_DATA far *vp = (BIOS_VIDEO_DATA far *) 0x00400049L;
                                       /*удаленная -> в область видео дан-
                                                              ных BIOS*/
        MOVE_XCHG mcb;                 /*структура передвижения/обмена
                                                         областей*/

        #pragma check_stack(off)
                                           /*очищается, если "control-c"*/
        void break_handler(sig_type)
        int sig_type;
        {
             signal(SIGINT, SIG_IGN);      /*запрещает "control-c" во время
                                                              драйвера*/
             cleanup;
             exit(0);
        }

             /*
                   драйвер прерывания для прерывания печати экрана
                   Делает моментальный снимок обычной памяти в
                   pасшиpенную память
             */
        void interrupt cdecl far int5_isr(es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,
                                                          cs,flags)
        unsigned  es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs,flags;
        {
             static int rc = 0;             /*отслеживается последний код
                                                               возврата*/
             int status;
             if (!isr_status)               /*если обслуживание прерывания
                                            не активизировано, подцепить к
                                          предыдущему драйверу прерывания*/

                                      - 7-78 -
                _chain_intr(old_int5);
             if (rc == 0)  {            /*продолжать только, если ранее не
                                                          было ошибок*/
                rc = EMSGetPageMap(emm_save);

                if (rc == 0)  {               /*контекст сохранен успешно*/

                                                  /*сделать снимок экрана*/
                   rc = dump_screen();
                          /*восстановить контекст предыдущего отображения*/

                   if (status = EMSSetPageMap(emm_save))
                      rc = status;                  /*обновить код ошибки*/
                }
             }
             /*если произошла какая-либо ошибка, объявить вслух*/

             if (rc)
                Beep(32000);
        }

        dump_screen()
        {
        int rc;
        unsigned int overflow, new_offset, scr_size;

                 /*отобразить индексные данные экрана в логической странице
                            0 в физическую страницу 0*/

             if (rc = EMSMapHandlePage(emm_handle, 0, 0))
                return(rc);                                       /*отказ*/

             /*убедитесь, что не вышли из индексных элементов*/

             if (ip->scr_count >= ip->scr_max)
                return(1);                         /*переполнение индекса*/

             /*если экран переполняет страницу, разместите одну или более
                                      дополнительных страниц*/

             scr_size = vp->crt_cols * SCR_ROWS * 2;      /*кол-во байтов в
                                                                   экране*/
             new_offset = sp->scr_offset + scr_size;      /*новое смещение
                                                      в буфере сохранения*/

             if (new_offset  > PAGE_SIZE) {  /*экран переполняет страницу*/

                overflow = new_offset - PAGE_SIZE;   /*величина переполне-
                                                             ния страницы*/
                emm_pages += (overflow / PAGE_SIZE);  /*кол-во необходимых
                                                   дополнительных страниц*/
                new_offset = overflow % PAGE_SIZE;   /*размер добавочного
                                                                фрагмента*/
                if (new_offset)          /*прибавить страницу на добавок,*/
                   emm_pages++;                         /*если необходимо*/

                if (rc = EMSReallocPages(emm_handle, &emm_pages))

                                      - 7-79 -
                   return(rc);                                   /*отказ*/
             }

             /*снимок, отображающий видеоэкран в расширенную память*/

             mcb.region_length = (long) scr_size;  /*кол-во байтов на экра-
             mcb.source.memory_type =  CONV_MEM;  не в обычной памяти*/
             mcb.source.handle      = 0;  /*никакой обpаботчик не исп-ся*/
             mcb.source.initial_offset = vp->crt_start;  /*начальное сме-
                                                         щение экрана*/
             mcb.source.initial_seg_page = (vp->crt_mode == 7) ? MDA_SEG :
                                                                   CGA_SEG;
                  /*работает только с цветным/монохромным текстом*/
             mcb.dest.memory_type        = EXP_MEM;       /*идет в расши-
                                                          ренную память*/
             mcb.dest.handle             = emm_handle;    /*предварительно
                                                 размещенный обpаботчик*/
             mcb.dest.initial_offset = sp->scr_offset; /*следующее до-
                                                     ступное смещение*/
             mcb.dest.initial_seg_page = sp->scr_page;  /*в текущей стра-
                                                                    нице*/

             if (rc = EMSMoveRegion(&mcb))  /*функция "Передвинуть область
                                 памяти" спецификации pасшиpенной памяти*/
                return(rc);                                     /*отказ*/

             Beep(1000);       /*выдать короткий звуковой сигнал - успех*/

             /*обновить индексные данные экрана ("Передвинуть область" не
                               нарушает контекста отображения*/

             ip->scr_count++;  /*уменьшение кол-ва сохраненных экранов*/
             sp->scr_len = src_size; /*запомнить кол-во байтов на экране*/
             sp->scr_width = vp->crt_cols;/*запомнить кол-во колонок в
                                            строке*/
             sp++;               /*указать на следующий индексный элемент*/
             sp->scr_len = 0;       /*новый еще не снятый экран,*/
             sp->scr_width = 0;      /*поэтому нулевые длина и ширина*/
             sp->scr_page = emm_pages -1;     /*новый экран идет на послед-
                                               нюю  размещенную страницу,*/
             sp->scr_offset = new_offset;     /*непосредственно следуя за
                                                       предыдущим экраном*/
             return(rc);                                       /*успех*/
        }

             /*освободить расширенную память, если размещена*/

        cleanup()
        {
             int rc;
             /*игнорировать код возврата, так как вызов может быть из
                             ошибочной процедуры*/
             if (emm_handle != -1)
                rc = EMSDeallocatePages(emm_handle);
        }

        #pragma check_stack(on)

                                      - 7-80 -

        main(argc, argv)
        int argc;
        char *argv[];
        {
             int emm_present, rc;
             unsigned int far *pseg_top;
             char emm_ver[4];

             get_opts(argc, argv);     /*получить переключатели командной
                                                                строки*/
             emm_handle = -1;          /*нет размещенных обpаботчиков
                                             pасшиpенной памяти*/
             /*установить драйвер "Control-C" (прерывание)*/

             signal(SIGINT, break_handler);

             /*проверка наличия pасшиpенной памяти*/

             if (rc = emm_exists(emm_present))   {     /*тест наличия
                                   менеджеpа pасшиpенной памяти не прошел*/
                printf("snapshot: EMM presence test failed, rc: %d", rc);
                exit(2);
             }

             if (!emm_present)   {   /*нет pасшиpенной памяти*/
                printf("snapshot: No expanded memory is present");
                exit(1);
             }

             /*получить версию спецификации pасшиpенной памяти, которую
                     поддерживает менеджеp pасшиpенной памяти*/

             if (rc = EMSGetVersion(emm_ver))   {
                DisplayError(rc);
                exit(1);
             }

             /*убедитесь, что это - по крайней мере, версия 4.0*/

             if (*emm_ver < '4') {    /*требуется спецификация pасшиpенной
                                                  памяти LIM 4.0 или выше*/
                printf("snapshot: Unsupported EMM version detected: %s,
                   LIM EMS 4.0 or greater is required", emm_ver);
                exit(1);
             }

             /*получить указатель на кадр страниц спецификации pасшиpенной
                               памяти*/

             if (rc = EMSGetFrameAddr(&page_frame))   {
                DisplayError(rc);
                exit(1);
             }

             /*поиск обpаботчика спецификации pасшиpенной памяти, который
                          содержит запомненные экраны*/

                                      - 7-81 -

             rc = EMSSearchHandleName(HANDLE_NAME, &emm_handle);

             /*ошибка, если любой код возврата, отличный от 'нормально'
                        или 'обpаботчик не найден'*/

             if (rc != 0 && rc != HNDVLNFD)   {
                DisplayError(rc);
                exit(1);
             }

             /*или закончить и остаться резидентно, установить программу,
             остающуюся после завершения резидентно, или показать ее текущее
                                состояние*/

             if (terminate_flag)  {    /*пользователь запросил завершение*/

                if (rc == 0)      /*обpаботчик с нашим именем существует,*/
                     terminate();        /*поэтому постараться снять себя*/
                else  {          /*обpаботчик не существует, поэтому не мо-
                                                 жет завершиться */
                printf("snapshot: can't terminate - not installed");
                exit(1);
                }

             } else  {           /*или установиться или выдать состояние*/
                /* если обработчик, именованный HANDLE_NAME, уже сущест-*/
                /*вует, только сообщить,  сколько памяти спецификации   */
                /*расшиpенной памяти в настоящий  момент размещено, и как*/
                /*много экранов хранится в ней. В противном случае, уста-*/
                /*новить программу обслуживания прерывания для прерывания*/
                /*печати и сделать данную программу резидентной*/

                   if (rc == 0) /*обpаботчик с нашим именем уже сущест-*/
                      show_status(); /*вует,поэтому только показать состоя-
                                                                      ние*/
                   else  {          /*обpаботчик не существует,*/
                      install();    /*поэтому разместить обpаботчик и уста-
                                   новить программу обработки прерывания*/

                           /*завершиться и остаться резидентно*/

                      FP_SEG(pseg_top) = _psp; /*конечный параграф програм-
                                                мы находится по psp+2 */
                      FP_OFF(pseg_top) = 2;

                      printf("snapshot: TSR installing at segment [%04X],
                       size %u paragraphs\n", _psp, *pseg_top  - _psp);

                      _dos_keep(0, *pseg_top - _psp);  /*кол-во параграфов
                                                            в программе*/
                   }
             }
        }

             /*отобразить идентификатор обpаботчика, кол-во логических*/
             /*страниц, размещенных для него, и кол-во экранов, запомнен-*/

                                      - 7-82 -
             /*ных в данный момент в pасшиpенной памяти*/

        show_status()
        {
             int rc;
             unsigned int alloc_pages, screens;

             /*определение кол-ва страниц, размещенных для данного обpа-*/
                       /*ботчика менеджеpа pасшиpенной памяти*/

             if (rc = EMSGetHandlePages(emm_handle, &alloc_pages))  {
                DisplayError(rc);
                cleanup();
                exit(1);
             }

             /*отобразить первую логическую страницу, содержащую индекс
               экрана, в нулевую физическую страницу*/

             if (rc = EMSMapHandlePage(emm_handle, 0, 0))  {
                DisplayError(rc);
                cleanup();
                exit(1);
             }

             /*получить адресуемость для структуры данных индексов экранов
                в pасшиpенной памяти*/

             ip = (SCR_INDEX far *) page_frame;

             /*печатать текущее состояние*/

                  printf("snapshot: status - EMS handle (%d); EMS
                     pages (%d); screens (%d)\n", emm_handle, alloc_pages,
                         ip->scr_count);

             /*перестать отображать страницу индексов экрана*/

             if (rc = EMSMapHandlePage(emm_handle, -1, 0))  {
                DisplayError(rc);
                cleanup();
                exit(1);
             }
        }

             /*Получить обpаботчик спецификации pасшиpенной памяти с од-*/
             /*ной страницей от менеджеpа pасшиpенной памяти и дать ему*/
             /*наименование, так чтобы другие программы могли обращаться*/
             /*к нему. Инициализировать структуру индексных данных экра-*/
             /*нов, которая будет размещена в начале первой страницы спе-*/
             /*цификации pасшиpенной памяти. Затем  подставьте программу*/
             /*обслуживания прерывания для прерывания печати экрана, так*/
             /*чтобы образы экранов сохранялись в pасшиpенной памяти, */
             /*когда пользователь нажимает клавишу PrtScr.*/

        install()


                                      - 7-83 -
        {
             int rc, context_bytes;

             /*разместить одну страницу для начала*/

             emm_pages = 1;

             if (rc = EMSAllocatePages(&emm_handle, emm_pages))   {
                DisplayError(rc);
                exit(1);
             }

             /*дать обpаботчику имя, так чтобы другие программы могли */
                                   /*  найти его */
             if (rc = EMSSeyHandleName(emm_handle, HANDLE_NAME))  {
                DisplayError(rc);
                cleanup();
                exit(1);
             }
             printf("snapshot: allocated expanded memory handle # %d with
                     name '%s'\n", emm_handle, HANDLE_NAME);

             /* инициализировать данные в странице индексов экранов,*/
             /* которые будут запоминаться в логической странице 0 */

             if (rc = EMSMapHandlePage(emm_handle, 0, 0))  {
                DisplayError(rc);
                cleanup();
                exit(1);
             }

             /*получить адресуемость для структуры данных индексов экранов
                в pасшиpенной памяти*/

             ip = (SCR_INDEX far *) page_frame;

             /* инициализировать данные в ней */

             ip->scr_count = 0;           /*кол-во  сохраненных экранов*/
             ip->scr_max = MAX_SCR;       /*максимальное кол-во  сохранен-
                                                              ных экранов*/
             ip->scr_int5 = int5_isr;     /*указатель на нашу программу
                                    обслуживания прерывания печати экрана*/
             sp = ip->scr_idx;             /* -> первый индексный элемент*/
             sp->scr_page = sizeof(*ip) / PAGE_SIZE;  /*экраны начинаются*/
             sp->scr_offset = sizeof(*ip) % PAGE_SIZE;
                                                      /*сразу за индексом*/
             sp->scr_len = 0;                         /*вначале пусто*/
             sp->scr_width = 0;

             /*перестать отображать страницу индексов экрана*/

             if (rc = EMSMapHandlePage(emm_handle, -1, 0))  {
                DisplayError(rc);
                cleanup();
                exit(1);
             }

                                      - 7-84 -

             /*разместить область сохранения контекста pасшиpенной памяти,
               используемую драйвером прерывания печати экрана*/

             if (rc = EMSGetPageMapSize(&context_bytes))  {
                DisplayError(rc);
                cleanup();
                exit(1);
             }

             if ((emm_save = (PMAP *) malloc(context_bytes)) == NULL)   {
                printf("snapshot: Couldn't allocate %d bytes for context
                     save area", context_bytes);
                cleanup();
                exit(1);
             }

             /*установить обpаботчик прерывания для перехватывания запросов
                                    печати экрана*/

             old_int5 = _dos_getvect(PRTSC_INT);   /*сохранить старый век-
                                                     тор прерывания*/
             _dos_setvect(PRTSC_INT, int5_isr);    /*установить новый век-
                                                     тор прерывания*/
        printf("snapshot: print screen interrupt handler is installed\n");

        isr_status = 1;
        /* пусть новый драйвер обслуживает прерывания*/
        printf("snapshot: print screen interrupt handler is activated\n");
             }

             /* снять программу, резидентную после завершения, из памяти
                       по запросу пользователя */

        terminate()
        {
            int rc;
            unsigned int tsr_psp;         /*префиксный программный сегмент
                            активной программы, резидентной по завершении*/

            unsigned int far *envptr;     /*указатель среды программы, ре-
                                             зидентной по завершении*/

            void (interrupt far *our_int5)(); /*адрес установленной прог-
                                        раммы, резидентной по завершении*/

             /* приостановить обработку прерываний печати экрана */

             isr_status = 0;
             printf("snapshot: print screen interrupt handler
                        deactivated\n");

             /* отображение в страницу, содержащую индекс экрана */

             if (rc = EMSMapHandlePage(emm_handle, 0, 0))  {
                DisplayError(rc);
                cleanup();

                                      - 7-85 -
                exit(1);
             }

             /*получить адресуемость для структуры данных индексов экра-*/
             /*нов в pасшиpенной памяти, так чтобы мы  могли получить ад-*/
             /*рес программы обслуживания прерывания, которую мы установ-*/
             /*вили, когда данная программа стартовала*/

             ip = (SCR_INDEX far *) page_frame;
             our_int5 = ip->scr_int5;          /*получить запомненный адрес
                                        программы обслуживания прерывания*/

             /* освободить pасшиpенную память */

             cleanup();
             printf("snapshot: expanded memory handle %d deallocated\n",
                        emm_handle);

             /* если никакой другой драйвер печати экрана не был установ-*/
             /*лен перед нами, отключить программу обслуживания прерыва-*/
             /*ния и вновь установить программу*/

             if (_dos_getvect(PRTSC_INT) == our_int5)  {  /*наша программа
                                         обслуживания прерывания - первая*/

                /*восстановить старый вектор прерывания печати экрана */

                _dos_setvect(PRTSC_INT, old_int5);
                printf("snapshot: old print screen interrupt handler
                           restored\n");

                /* освободить строки среды программы, резидентной по завер-
                   шении, и программный сегмент*/

                tsr_psr = FP_SEG(our_int5) - 16;  /*PSP начинается за 1666
                                              параграфов до сегмента кода*/
                printf("snapshot: deallocating TSR at segment [%04X]\n",
                           tsr_psp);

                FP_SEG(envptr) = tsr_psp;    /*указатель среды - по смеще-
                                                                 нию*/
                FP_OFF(envptr) = 0x2C;       /* 2Ch в префикс сегмента про-
                                                              граммы*/
                _dos_freemem(*envptr);       /*освободить строки среды*/
                _dos_freemem(tsr_psp);       /*освободить программный сег-
                                                          мент*/

             } else  /*наша программа обслуживания прерывания не является
                       первой в цепи, нельзя снимать программу, резидентную
                                после завершения*/
                printf("snapshot: cannot deallocate TSR - print screen ISR
                           is not first in chain\n");
        }
             /*процесс командной строки переключается в форму /L,  где */
             /*'L' - идентификация переключателя по одному символу. Воз-*/
             /*вращается индекс первого элемента в массиве указателей, */
             /*следующем за переключателями*/

                                      - 7-86 -

        get_opts(cnt, ptr)
        int cnt;
        char *ptr;
        {
             int argc;

             terminate_flag = 0;          /*сбросить флаг завершения*/

             arc = 1;
             while (*++ptr)[0] == '/'  {
                switch(*ptr)[1])  {

                   case '?'               /*команда отображения и исполь-
                                            зование переключателей*/
                      printf("snapshot: saves text screen images to
                                expanded memory\n");
                      printf("usage: snapshot [/X]\n");
                      printf("   /X - terminates snapshot");
                      exit(0);
                      break;

                   case 'x':                 /*завершает запрос*/
                   case 'X':

                      terminate_flag = 1;
                      break;
                   default:                  /*неизвестный переключатель*/
                      printf("'%c' is an unknown option\n", (*ptr)[1]));
                      break;
                }
                argc++;
             }
             return(argc);
        }
        -------------------------------------------------------------------







                             Листинг 7-9. PLAYBACK.C
        -------------------------------------------------------------------
            /*
                 Имя: PLAYBACK.C
                 Назначение: Распечатка образов текстовых экранов, сохра-
                 ненных в pасшиpенной памяти программой SNAPSHOT, в стан-
                 дартный выходной файл DOS.
            */

        #include 
        #include 
        #include 
        #include 
        #include 

                                      - 7-87 -

        #include "emmconst.h"    /*константы менеджеpа pасшиpенной памяти*/
        #include "emmtypes.h"    /*структуры данных менеджеpа pасшиpенной
                                                                 памяти*/
        #include "emmerr.h"    /*коды ошибок менеджеpа pасшиpенной памяти*/
        #include "emmfunc.h"     /*объявления функций менеджеpа pасшиpенной
                                                                 памяти*/
        #define DisplayError(rc)  ShowEMMErr(rc, __LINE__, __FILE__)

        #define HANDLE_NAME       "SNAPSHOT"  /*имя обpаботчика pасшиpенной
                                                                  памяти/*
        #define MAX_SCR           500         /*максимальное кол-во сохра-
                                                        няемых экранов*/
        #define SCR_COLS          80          /*предполагается 80 колонок,
                                               позже можно корректировать*/
        #pragma pack(1)                       /*структуры данных с байтовым
                                                         выравниванием*/

        typedef struct scr {              /*дескриптор данных экрана*/
             unsigned int   scr_page;     /*страница pасшиpенной памяти для
                                                        начального экрана*/
             unsigned int   scr_offset;   /*смещение pасшиpенной памяти для
                                                        начального экрана*/
             unsigned int   scr_width;    /*кол-во колонок на экране*/

             unsigned int   scr_len;      /*длина экрана в байтах*/
        } SCR;

        typedef struct scr_index {          /*индексная структура экранов*/
             void (interrupt far *scr_int5)(); /*указатель на нашу програм-
                                               му обслуживания прерывания*/
             unsigned int   scr_count;         /*текущее кол-во сохраненных
                                                                экранов*/
             unsigned int   scr_max;           /*максимальное кол-во сохра-
                                                    ненных экранов*/
             SCR            scr_idx[MAX_SCR];  /*массив индексов экранов*/
        } SCR_INDEX;

             /*
                     Глобальные данные
             */

        unsigned int  emm_handle,    /*обpаботчик pасшиpенной памяти*/
                        emm_pages;   /*кол-во страниц, принадлежащих
                                                      обpаботчику*/
        char far       *page_frame;  /*удаленный -> на кадр страниц
                                   спецификации pасшиpенной памяти*/
        SCR_INDEX      far *ip;      /*удаленный -> на индекс экрана*/
        SCR            far *sp;      /*удаленный -> на дескриптор эк-
                                                             рана*/

        MOVE_XCHG mcb;     /*структура "Передвинуть/обменять области*/

        main()
        {
             unsigned int   scan_code;
             int emm_present, rc, current_screen;

                                      - 7-88 -
             char emm_ver[4];

             /*тест наличия pасшиpенной памяти*/

             if (rc = emm_exists(&emm_present))   {
                    /*тест наличия менеджеpа pасшиpенной памяти не прошел*/
                 printf("replay: EMM presence test failed, rc: %d", rc);
                 exit(2);
             }

             if (!emm_present)   {
                    /* pасшиpенной памяти нет */
                 printf("replay: No expanded memory is present");
                 exit(1);
             }

             /*получить версию спецификации pасшиpенной памяти,поддерживае-
                 мую данным менеджеpом pасшиpенной памяти*/

             if (rc = EMSGetVersion(emm_ver))   {
                DisplayError(rc);
                 exit(1);
             }

             /* убедитесь, что версия не ниже 4.0 */

             if (*emm_ver < '4')  {    /*требуется спецификация pасшиpенной
                                       памяти LIM не ниже 4.0*/
                 printf("replay: Unsupported EMM version detected: %s,
                    LIM EMS 4.0 or greater is required", emm_ver);
                 exit(1);
             }

             /* получить указатель на кадр страниц спецификации  pасшиpен-
                           ной памяти */

             if (rc = EMSGetFrameAddr(&page_frame))   {
                DisplayError(rc);
                 exit(1);
             }
             /* Поиск обpаботчика, содержащего запомненные экраны */

             if (rc = EMSSearchHandleName(HANDLE_NAME, &emm_handle))   {
                DisplayError(rc);
                 exit(1);
             }

             /* отобразить в страницу, содержащую индекс экрана */

             if (rc = EMSMapHandlePage(emm_handle, 0, 0))   {
                DisplayError(rc);
                 exit(1);
             }

             /* получить адресуемость на структуру данных индексов
                      экранов */


                                      - 7-89 -
             ip = (SCR_INDEX far *) page_frame;
             sp = ip->scr_idx;      /*указать на первый запомненный экран*/

             if (ip->scr_count == 0)
                printf("replay: no screens have been saved");
             else
                /*
                   распечатывать каждый запомненный экран на стандартный
                   вывод
                */
             for (current_screen = 0; current_screen < ip->scr_count;
                  current_screen++)  {
                rc = print_screen(sp++);

             if (rc)  {             /*произошла ошибка pасшиpенной памяти*/
                   DisplayError(rc);
                    exit(1);
                }
             }

             /* перестать отображать страницу индексов экранов */

             if (rc = EMSMapHandlePage(emm_handle, -1, 0))   {
                DisplayError(rc);
                 exit(1);
             }
        }

             /*
                 По данному удаленному указателю на дескриптор экрана, за-
                 помненный в pасшиpенной памяти, пишите каждый символ за-
                 помненного образа экрана в стандартный выходной файл DOS
             */

        print_screen(sp)
        SCR far *sp;               /*удаленный -> на дескриптор экрана*/
        {
             int rc, lpages, line, rows;
             char *line_buf[SCR_COLS+1];
             int far *bp;
             struct SREGS segregs;

             /* вычислить, сколько физических страниц нужно отобразить */

             lpages = 1;           /* по крайней мере, одна страница */
             if (sp->scr_offset + sp->scr_len > PAGE_SIZE)
                  lpages++;

             /* отобразить логическую страницу (страницы), которые содержат
             образ экрана, в физические страницы, начинающиеся с физической
                                  страницы 1 */

             for (i = 0; i < lpages; i++)
                if (rc = EMSMapHandlePage(emm_handle, i + sp->scr_page,
                                              i + 1))
                   return(rc)                          /* неудача */


                                      - 7-90 -
             /* получить адресуемость на физическую страницу 1 */

             bp = (int far *) page_frame;          /* базовый адрес кадра
                                                                 страниц*/
             FP_SEG(bp) += (PAGE_SIZE / 16);       /*кол-во параграфов в
                                 странице спецификации pасшиpенной памяти*/
             FP_OFF(bp) = sp->scr_offset;

             rows = sp->scr_len / sp->scr_width / 2;  /*вычисление кол-ва
                                                          строк на экране*/
             putchar('[bs]014');                  /*начать новую страницу*/

             /* записывать каждый символ на образе экрана в стандартный
                                       выход */

             for (line = 0; line < rows; line++) {
                i = sp->scr_width;
                while (i--)
                   putchar(*bp++ & 0xFF);
                putchar('[bs]n');           /* вывод новой строки после
                                                    каждой строки */
             }
             return(rc);
        }
        -------------------------------------------------------------------


                              Листинг 7-10. BEEP.ASM
        -------------------------------------------------------------------
             TITLE   Beep
        _TEXT           SEGMENT         BYTE PUBLIC 'CODE'
        _TEXT           ENDS
        _DATA           SEGMENT  WORD PUBLIC 'DATA'
        _DATA           ENDS
        CONST    SEGMENT          WORD PUBLIC 'CONST'
        CONST    ENDS
        _BSS            SEGMENT  WORD PUBLIC 'BSS'
        _BSS            ENDS
        DGROUP  GROUP    CONST, _BSS,       _DATA
                ASSUME   CS:_TEXT, DS:DGROUP, SS:DGROUP, ES:DGROUP
        _TEXT           SEGMENT
        timer   equ     40h
        port_b          equ      61h
        ;-----  Издает слышимый звук на внутреннем громкоговорителе ПЭВМ
        ;       Длительность звука управляется одним целым аргументом.
        ;
                PUBLIC  _Beep
                PUBLIC  _Beep
        _Beep   PROC    NEAR
                push    bp
                mov     bp,sp
                mov     al,10110110B   ;генерируется короткий звук (длинный
        ;                               теряет данные)
                out     timer+3,al     ;код, взятый из технич-го описания
                mov     ax,533H
                out     timer+2,al
                mov     al,ah

                                      - 7-91 -
                out     timer+2,al
                in      al,port_b
                mov     ah,al
                or      al,03
                out     port_b,al
                mov     cx,[bp+4]
                mov     bl,1
        beep0:  loop    beep0
                dec     bl
                jnz     beep0
                mov     al,ah
                out     port_b,al
                pop     bp
                ret
        _Beep           ENDP
        _TEXT           ENDS
                END

© KOAP Open Portal 2000
 
 


?????? ???????????