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

Оверлеи полезны только в программах  DOS  реального режима.  Поскольку для программ  Windows памятью управляет сама Windows, а  для программ защищенного режима - администратор этапа  выполнения  (RTM.EXE), эти  средства включают в себя полный механизм обслуживания оверлеев,  и в программах Windows и программах  защищенного  режима необходимость использования оверлеев отпадает.

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

Borland Pascal управляет оверлеями на уровне модулей,  которые являются наименьшей частью программы, образующей оверлей. При  компиляции   программы,  имеющей  оверлейную структуру,  Borland  Pascal генерирует наряду с выполняемым файлом (который имеет расширение  .EXE) оверлейный файл (имеющий расширение .OVR).  Файл с  расширением .EXE содержит статические (не оверлейные) части программы,  а файл с расширением .OVR содержит все оверлейные модули,  которые при выполнении программы будут подкачиваться в память или  выводиться из нее на диск.

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

При загрузке  оверлеев в  память они помещаются в оверлейный  буфер, который размещается в памяти между сегментом стека и динамически распределяемой областью памяти. По умолчанию для оверлейного буфера выбирается минимальный возможный размер,  но во время  выполнения  программы  его размер может быть легко увеличен путем  выделения дополнительной области памяти из динамически  распределяемой области. Аналогично сегменту данных и минимальному размеру  динамически распределяемой области, оверлейный буфер принятого по  умолчанию размера выделяется при загрузке файла .EXE. При отсутствии памяти необходимого объема модулем Dos  или интегрированной  программной средой  IDE будет  выводиться  сообщение об  ошибке  (Program  too big  to fit in memory - "Программа слишком велика,  чтобы разместиться в памяти")  или (Not  enough  memory to  run  program - "Для запуска программы не хватает памяти").

Одной из очень  важных возможностей  подсистемы  управления  оверлеями  является  возможность  при наличии достаточного пространства загружать  оверлейный файл в дополнительную память. Для  этой цели в Borland Pascal поддерживается средство расширения памяти  EMS (Lotus/Intel/Microsoft Expanded Memory Specification).  При размещении оверлейного файла в  памяти  EMS  все  последующие  загрузки оверлеев сводятся к быстрой передаче информации из памяти в память.

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

Для реализации  улучшенных методов   управления   оверлеями  Borland Pascal требует от вас при написании программы,  в которой  используются оверлеи, соблюдать два важных правила:

1.  Все оверлейные модули должны содержать  директиву {$O+},  приводящую к тому, что компилятор обеспечивает генерирование оверлейного кода.

2.  При  каждом обращении к оверлейной процедуре или функции  вы должны обеспечить использование всеми активными  процедурами и функциями вызовов типа FAR (дальний тип вызова).

Оба правила будут поясняться далее в разделе  под заголовком  "Разработка оверлейных программ".  Сейчас мы просто отметим,  что  вы можете легко удовлетворить  эти правила,  поместив  в начале  оверлейных  модулей  директиву  компилятора {$O+,F+},  а в начале  всех других модулей и основной программы - директиву {$F+}.

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

Директива компилятора {$O  имя_модуля} используется в программе для указания того,  какой из модулей будет оверлейным.  Эта  директива должна размещаться за оператором uses программы, в котором перед именами всех других оверлейных  модулей  должно указываться  имя стандартного модуля Overlay. Приведем следующий пример:

program Editor;

{$F+}  { Все процедуры и функции будут использовать дальний тип вызова }

uses

Overlay, Crt, Dos, EdInOut, EdFormat, EdPrint, EdFind,

EdMain;

{$O EdInOut }

{$O EdFormat }

{$O EdPrint }

{$O EdFind }

{$O EdMain }

Если вы  пытаетесь использовать  в качестве оверлейного модуль, при компиляции которого не была указана директива {$O+}, то  компилятор выведет сообщение об ошибке.  Что касается стандартных  модулей, то оверлейным может быть только модуль Dos. Другие стандартные  модули не могут использоваться в качестве оверлейных.  К  тому же программы,  содержащие оверлейные модули, при использовании IDE реального режима должны компилироваться на диск.  Если вы  пытаетесь выполнить компиляцию таких программ в память, то компилятор выводит сообщение об ошибке.

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

Поскольку обычная память по своей природе не имеет характера  предусматривает несколько шагов,  обеспечивающих,  чтобы  буфер  кольцевого буфера,  действительная реализация  кольцевого буфера  действительно стал кольцевым.  Этот процесс показан на Рис. 20.1.  Здесь изображен процесс загрузки оверлеев в  первоначально пустой  оверлейный буфер.  Сначала загружается оверлей A, затем - оверлей  B, потом C, и, наконец, D. Заштрихованные области показывают свободное пространство в буфере.

Шаг 1 Шаг 2

   ЪДДДДДДДДДДДДї   ЪДДДДДДДДДДДДї

   і °°°°°°°°°° і   і °°°°°°°°°° і

   і °°°°°°°°°° і   і °°°°°°°°°° і

   і °°°°°°°°°° і   і °°°°°°°°°° і

   і °°°°°°°°°° і   Начало ДДД> ГДДДДДДДДДДДДґ

   і °°°°°°°°°° і   і  Оверлей B і

  Начало ДДДД> ГДДДДДДДДДДДДґ  ГДДДДДДДДДДДДґ

   і  Оверлей А і   і  Оверлей А і

  Конец  ДДДД> АДДДДДДДДДДДДЩ  Конец  ДДД> АДДДДДДДДДДДДЩ

Шаг 3 Шаг 4

   ЪДДДДДДДДДДДДї   ЪДДДДДДДДДДДДї

   і °°°°°°°°°° і   і  Оверлей С і

   і °°°°°°°°°° і   ГДДДДДДДДДДДДґ

  Начало ДДДД> ГДДДДДДДДДДДДґ   і  Оверлей В і

  і  Оверлей С і   Конец  ДДД> ГДДДДДДДДДДДДґ

   ГДДДДДДДДДДДДґ   і °°°°°°°°°° і

   і  Оверлей В і   і °°°°°°°°°° і

   ГДДДДДДДДДДДДґ   Начало ДДД> ГДДДДДДДДДДДДґ

   і  Оверлей А і   і  Оверлей D і

  Конец  ДДДД> АДДДДДДДДДДДДЩ   АДДДДДДДДДДДДЩ

Рис. 20.1 Загрузка и освобождение оверлеев.

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

Этот режим  операция используется  администратором оверлеев  Borland Pascal 0 по умолчанию.  Однако, Borland Pascal также позволяет  вам использовать возможность оптимизации алгоритма управления оверлеями.

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

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

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

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

В модуле Overlay определяются несколько процедур и  функций.  Полные их описания вы можете найти в Главе 1 ("Справочник по библиотеке") "Справочного руководства программиста".

  Процедуры и функции модуля Overlay Таблица 20.1

 ЪДДДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДї

  іПодпрограмма   і Описание і

 ГДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДґ

  і  OvrClearBuf  і Очищает оверлейный буфер.  і

 ГДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДґ

  і  OvrGetBuf і Возвращает текущий размер оверлейно-і

  і    і го буфера.  і

 ГДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДґ

  і  OvrGetRetry  і Возвращает  текущий  размер пробнойі

  і і области (последнее  значение, уста-і

  і і новленное OvrSetRetry). і

 ГДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДґ

  і  OvtInit   і Эта процедура инициализирует подсис-і

  і    і тему управления оверлеями и открыва-і

  і і ет оверлейный файл.  і

 ГДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДґ

  і  OvrInitEMS   і Данная процедура, если это возможно,і

  і і загружает оверлейный файл  в памятьі

  і і EMS. При  этом все последующие заг-і

  і і рузки оверлеев  сводятся  к  быстройі

     іі передаче  информации из памяти в па-і

  і і мять. і

 ГДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДґ

  і  OvrSetBuf і Устанавливает размер оверлейного бу-і

  і і фера. і

 ГДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДґ

  і  OvrSetRetry  і Задает   размер  пробной области  ві

  і і оверлейном буфере.   і

 АДДДДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДЩ

Константы и переменные модуля Overlay

  ДДДДДДД

В модуле Overlay определены пять переменных:

Переменные модуля Overlay   Таблица 20.2

  ЪДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДї

  і  Переменная   і Описание і

 ГДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДґ

  і  OvrFileMode  і Определяет передаваемый DOS при  откры-і

  іі тии файла код доступа. і

 ГДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДґ

  і  OvrLoadCount і Данная   переменная увеличивается  приі

  і і каждой загрузке оверлея.   і

 ГДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДґ

  і  OvrReadBuf   і Эта  процедурная переменная  позволяеті

  і і вам интерпретировать операции  загрузкиі

  і і оверлея. і

 ГДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДґ

  і  OvrResult і Перед  возвратом управления каждая про-і

  і    і цедура в модуле Overlay сохраняет  свойі

  і і код результата в переменной OvrResult. і

 ГДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДґ

  і  OvrTrapCount і Каждый раз,  когда обращение к подпрог-і

  і і рамме оверлея перехватывается подсисте-і

  і і мой управления оверлеями (когда оверлеяі

  і і нет в памяти или он находится на тести-і

  і    і ровании) значение   переменнойі

  і і OvrTrapCount  увеличивается.  Начальноеі

  і і ее значение равно 0. і

 АДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДЩ

Значения этих  переменных вы можете найти в Главе 1 ("Справочник по библиотеке") "Справочного руководства программиста".

Об ошибках   модуль  Overlay   сообщает   через переменную  OvrResult. См. константы ovrXXXX  в Главе 1 ("Справочник  по библиотеке") "Справочного руководства программиста".

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

Borland Pascal допускает  использование  модуля  в  качестве  оверлейного только в том случае, если он генерировался с директивой {$O+}. Когда задана эта директива, генератор выполняемого кода, при передаче строки из одной оверлейной процедуры в другую и  задании постоянных параметров,  предпринимает особые меры предосторожности.  Например,  если модуль UnitA содержит  процедуру  со  следующим заголовком:

procedure WriteStr(s: string);

  и модуль UnitB содержит оператор:

WriteStr('Hello word...');

  то Borland Pascal  помещает строковую константу 'Hello word...' в  сегмент кода модуля UnitB и передает указатель на  него процедуре  WriteStr.  Однако,  если оба модуля являются оверлейными,  то это  работать не будет, поскольку при обращении в WriteStr сегмент кода модуля  UnitB может быть перекрыт модулем UnitA,  и ссылка на  строку окажется недопустимой.  Для того, чтобы избежать эти проблемы, используется директива {$O+}.  Каждый раз, когда Турбо Паскаль встречает обращение из одного  модуля,  скомпилированного  с  директивой {$O+},  к другому модулю, скомпилированному с директивой {$O+},  компилятор перед передачей ссылок на них обеспечивает  временное копирование всех размещенных в сегменте кода констант в  стек.

 

Указание в модуле директивы {$O+} не обязывает вас использовать этот  модуль  как оверлейный.  Она просто указывает Borland  Pascal на необходимость обеспечения,  если это нужно, использования данного модуля в качестве оверлейного. Если вы разрабатываете  модули, которые планируете использовать как в оверлейных, так и в  неоверлейных прикладных программах, то компиляция их с директивой {$O+} обеспечивает использование одной версии  модуля  для  обоих  случаев.

 

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

Это можно  хорошо проиллюстрировать  на  следующем примере.  Предположим,  что OvrA представляет собой процедуру в  оверлейном  модуле, а процедуры MainC и MainD - процедуры в основной программе. Если основная программа вызывает MainC, которая вызывает процедуру MainB, которая в свою очередь обращается к процедуре OvrA,  то во время обращения к процедуре OvrA процедуры  MainC  и MainB  являются активными (они еще не выполнили возврат управления), поэтому необходимо использовать для них дальний тип вызова. Описанные в основной программе, процедуры MainC и MainB в обычной ситуации используют ближний тип вызовов (NEAR).  С помощью  директивы  компилятора {$F+} необходимо задать дальний тип вызовов.

Самый легкий  способ удовлетворения требования использования  дальнего типа вызовов состоит  в размещении  в  начале основной  программы и в начале каждого модуля директивы {$F+}. Альтернативный способ состоит в изменении принятой по умолчанию установки $F  на {$F+}  с помощью директивы командной строки /$F+ или с помощью  параметра Force Far Calls (Использовать дальний тип вызова) в диалоговом  меню  OptionsіCompiler (ПараметрыіКомпилятор) среды IDE  интерактивного компилятора. По сравнению со смешанным использованием вызовов ближнего и дальнего типа использование вызовов только типа FAR не приводит к особенно большим дополнительным  затратам памяти:  для этого требуется одно дополнительное слово пространства стека на активную процедуру и один дополнительный байт на  каждый вызов.

Здесь мы рассмотрим некоторые примеры того,  как инициализируется администратор оверлеев (подсистема управления  оверлеями).  Код инициализации  должен быть помещен перед первым обращением к  оверлейной программе.  Инициализацию обычно следует делать в операторной части программы.

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

begin

   OvrInit('EDITOR.OVR');

end;

Проверка на ошибки не делается. Поэтому если для оверлейного  буфера  не  хватает памяти или оверлейный файл не найден,  то при  попытке  вызова  оверлейной  программы произойдет   ошибка   208  (Overlay manager not installed - "Администратор оверлеев не установлен").

Приведем другой  небольшой пример,  являющийся  расширением  предыдущего.

begin

   OvrInit('EDITOR.OVR');

   OvrInitEMS;

end;

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

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

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

  OvrMaxSize = 80000;

begin

   OvrInit('EDITOR.OVR');

   OvrInitEMS;

   OvrSetBuf(OvrMaxSize);

end;

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

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

Нужно также помнить о том,  что процедура OvrSetBuf увеличивает размер оверлейного буфера за счет уменьшения размера динамически распределяемой области памяти.  Таким образом,  динамически  распределяемая   область должна  быть  пустой, иначе  процедура OvrSetBuf не окажет никакого действия. Если вы используете модуль  Graph,  убедитесь в том, что вы обращаетесь к процедуре OvrSetBuf  перед вызовом процедуры InitGraph,  которая выделяет память в динамически распределяемой области.

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

const

   OvrMaxSize = 80000;

var

   OvrName: string[79];

   Size: Longint;

begin

   OvrName:='EDITOR.OVR';

   repeat

  OvrInit(OvrName);

  if OvrResult=ovrNotFound then

  begin

WriteLn('Оверлейный файл не найден');

WriteLn('Введите правильное имя оверлейного файла:');

ReadLn(OvrName);

  end;

until OvrResult<>ovrNotFound;

if OvrResult<>ovrOk then

begin

   WriteLn('Ошибка администратора оверлеев.')

   Halt(1);

end;

OvrInEMS;

if OvrResult<>OvrOk then

begin

   case OvrResult of

  ovrIOError:   Write('Ошибка ввода-вывода',

  ' оверлейного файла');

  ovrNoEMSDriver:  Write('Драйвер EMS не',

  ' установлен');

  ovrNoEMSMemory:  Write('Не хватает расширенной',

  ' памяти');

end;

Write('. Нажмите клавишу Enter...');

ReadLn;

  end;

  OvrSetBuf(OvrMaxSize);

end;

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

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

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

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

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

Взяв в  качестве  примера  ранее  рассмотренную   программу  Editor,  предположим,  что  модули EdInOut и EdMain содержат код  инициализации.  При этом требуется, чтобы процедура OvrInit вызывалась  перед кодом инициализации модуля EdInOut,  и единственный  способ осуществить это состоит во введении  дополнительного  неоверлейного  модуля,  который  следует перед EdInOut и вызывает в  своем разделе инициализации процедуру OvrInit.

unit EdInit;

interface

implementation

uses Overlay;

const

   OvrMaxSize = 80000;

begin

   OvrInit('EDITOR.OVR');

   OvrInitEMS;

   OvrSetBuf(OvrMaxSize);

end.

В операторе uses программы модуль  EdInit  должен  следовать  перед всеми оверлейными модулями:

program Editor;

{$F}

uses

 Overlay,Crt,Dos,EdInit,EdInOut,EdFormat,EdPrint,EdMain;

{$O EdInOut }

{$O EdFormat }

{$O EdPrint }

{$O EdFind }

{$O EdMain }

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

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

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

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

1.  Модули, скомпилированные с директивой {$O-}. Если вы пытаетесь использовать как оверлейный модуль,  который  не  был скомпилирован с директивой {$O+},  то компилятор выдает сообщение об ошибке.  Такими неоверлейными модулями  являются модули System,  Overlay, Crt,  Graph, Turbo3 и  Graph3.

2.  Модули,  которые содержат драйверы прерываний. Из-за того, что сама операционная система DOS имеет неоверлейную  структуру,   модули, реализующие  процедуры  прерываний  (interrupt), не должны быть оверлейными. В качестве примера  такого модуля  можно  привести стандартный модуль  Crt, реализующий драйвер обработки прерывания, возникающего при нажатии клавиш Ctrl+Break.

3.  Драйверы  BGI  или шрифты, зарегистрированные с помощью  вызова   подпрограмм   RegisterBGIdriverили  RegisterBGIfont.

Администратором оверлеев Borland Pascal полностью  поддерживается вызов оверлейных процедур с помощью указателей процедур. В  качестве примеров использования указателей процедур можно привести процедуры  завершения и драйверы устройств для текстовых файлов.

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

Большинство отладчиков обладают весьма ограниченными возможностями отладки оверлеев,  если они вообще обладают такими средствами. Этого нельзя сказать о Borland Pascal  и  Турбо  отладчике  (Turbo Debugger).  Встроенный отладчик полностью поддерживает при  работе с оверлеями пошаговый режим и  точки  останова,  используя  при этом метод,  полностью прозрачный для пользователя. С помощью  оверлеев вы легко можете конструировать и  отлаживать  прикладные  пакеты большого объема.  Все это можно делать как с помощью Турбо  отладчика, так и из интерактивной среды компилятора IDE.

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

Если в  программе на языке ассемблера осуществляется обращение к любой оверлейной процедуре или функции,  то в программе ассемблера должен  использоваться  дальний тип вызова,  и с помощью  регистра BP должны  быть установлены  границы  стека. Например,  предположим,  что OtherProc является оверлейной процедурой в другом модуле и ее вызывает программа ExternProc на языке  ассемблера. Тогда программа ExternProc должна иметь дальний тип вызова и  устанавливать границы стека следующим образом:

ExternProc   PROC FAR

PUSH   bp   ; сохранить регистр ВР

mov bp,sp   ; установить границы стека

SUB sp,LocalSize ; выделить локальные

;   переменные

...

CALL   OtherProc  ; вызвать другой оверлейный

    ; модуль

...

mov sp,bp   ; отменить локальные переменные

pop bp   ; восстановить регистр ВР

RET ParamSize  ; возврат управления

    ExternProc  ENDP

  где LocalSize представляет собой размер локальных  переменных,  а  ParamSize  - размер параметров.  Если значение LocalSize равно 0,  то две строки,  в которых выделяются и уничтожаются локальные переменные, можно опустить.

Если в  программе  ExternProc  имеются косвенные  ссылки на  оверлейные процедуры и функции,  то эти требования остаются  теми  же. Например, если процедура OtherProc вызывает оверлейные процедуры или функции,  но сама не является оверлейной,  то программа  ExternProc должна, тем не менее, иметь дальний тип вызова и устанавливать границы стека.

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

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

Переменная OvrReadBuf  позволяет вам перехватывать операции  загрузки оверлеев. Например, вы можете реализовать обработку ошибок или  проверку  наличия  сменного диска.  Когда администратору  оверлеев требуется считать оверлей,  он вызывает  функцию, адрес  которой записан  в OverReadBuf.  Если функция возвращает нулевое  значение, то администратор оверлеев  предполагает,  что операция  была успешной.  Если функция возвращает ненулевой результат,  то компилятор  генерирует  ошибку этапа  выполнения  209. Параметр  OvrSeg указывает,  какой именно оверлей требуется загрузить,  но,  как вы далее увидите, вам эта информация не потребуется.

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

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

Код для установки функции чтения  оверлея  должен следовать  непосредственно после  вызова OvrInit;  в этот момент OvrReadBuf  будет содержать адрес используемой по умолчанию функции чтения  с  диска.

Если вы  также  вызываете  OvrInitEMS, она  использует вашу  функцию чтения для чтения оверлеев с диска в память EMS, и в случае отсутствия  ошибок сохраняет адрес используемой по умолчанию  функции чтения в EMS в OvrReadBuf. Если вы хотите также переопределить функцию  чтения в  EMS,  просто  повторите  после вызова  OvrInitEMS процесс установки.

Используемая по умолчанию функция чтения с  диска  в случае  успешного выполнения возвращает 0. В противном случае возвращается код ошибки DOS.  Аналогично, используемая по умолчанию функция  чтения из EMS в случае успешного выполнения возвращает 0.  В противном случае возвращается код ошибки EMS (от $80 до  $FF).  Подробно коды ошибок DOS описываются в "Справочном руководстве программиста". Коды ошибок EMS можно найти в документации по EMS.

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

Все ошибки  передаются процедурам DOSError или EMSError (которые здесь не показаны),  которые могут вывести ошибку пользователю. Заметим,  что параметр OvrSeg просто передается сохраненной  функции чтения оверлея и не обрабатывается непосредственно  новой  функцией чтения оверлея.

uses Overlay;

var

SaveOvrRead: OvrReadFunc;

UsingEMS: Boolean;

 

function MyOvrRead(OvrSeg: Word): Integer: far;

var

  E: Integer;

begin

repeat

   E := SaveOvrRead(OvrSeg);

   if E <> 0 then

if UsingEMS then

EMSError(E) else DOSError(E);

    until E = 0;

MyOvrRead := 0;

end;

 

begin

OvrInit('MYPROG.OVR');

SaveOvrRead := OvrReadBuf;  { сохранить }

OvrReadBuf := MyOvrRead; { установить свою }

UsingEMS := False;

OvrInitEMS;

if OvrResult = OvrOK then

begin

   SaveOvrRead := OvrReadBuf   { сохранение }

   OvrReadBuf := MyOvrRead; { установить свою }

   UsingEMS := True;

end;

  .

  .

  .

end.

Borland Pascal также позволяет вам записывать оверлеи в  конец выполняемого файла .EXE прикладной программы,  а не в отдельный файл .OVR.  Чтобы присоединить файл .OVR к концу файла  .EXE,  используйте  команду  DOS  COPY с параметром командной строки /B,  например:

COPY/B MYPROG.EXE + MYPROG.OVR

Вы должны убедиться,  что файл .EXE компилировался без включения в него информации для отладки.  Таким образом, в интегрированной интерактивной  среде IDE в меню OptionsіCompiler (ПараметрыіКомпилятор) проверьте параметр Standalone  (Автономная  отладка). При использовании компилятора, работающего с командной строкой, укажите параметр /V.

Для чтения оверлея не из отдельного файла .OVR,  а из  конца  файла .EXE просто задайте при вызове OvrInit имя файла .EXE. Если  вы работаете под управлением DOS версии 3.х,  то можете использовать для получения имени файла .EXE стандартную функцию ParamStr,  например:

OvrInit(ParamStr(0));