Оверлеи представляют собой части программы, которые совмест- но используют общую область памяти. В один и тот же момент време- ни резидентно размещаться в памяти может та или иная часть прог- раммы, необходимая для выполнения заданной функции. В процессе выполнения эти части программы могут замещать друг друга. Оверлеи полезны только в программах 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 ¦ Начало ----> +------------+ +------------+ ¦ Оверлей А ¦ ¦ Оверлей А ¦ Конец ----> L------------- Конец ---> L------------- Шаг 3 Шаг 4 ------------- ------------- ¦ ---------- ¦ ¦ Оверлей С ¦ ¦ ---------- ¦ +------------+ Начало ----> +------------+ ¦ Оверлей В ¦ ¦ Оверлей С ¦ Конец ---> +------------+ +------------+ ¦ ---------- ¦ ¦ Оверлей В ¦ ¦ ---------- ¦ +------------+ Начало ---> +------------+ ¦ Оверлей А ¦ ¦ Оверлей D ¦ Конец ----> L------------- L------------- Рис. 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
В модуле Overlay определяются несколько процедур и функций. Полные их описания вы можете найти в Главе 1 ("Справочник по биб- лиотеке") "Справочного руководства программиста". Процедуры и функции модуля Overlay Таблица 20.1 --------------------------T------------------------------------- ¦ Подпрограмма ¦ Описание ¦ +-------------------------+-------------------------------------+ ¦ OvrClearBuf ¦ Очищает оверлейный буфер. ¦ +-------------------------+-------------------------------------+ ¦ OvrGetBuf ¦ Возвращает текущий размер оверлейно-¦ ¦ ¦ го буфера. ¦ +-------------------------+-------------------------------------+ ¦ OvrGetRetry ¦ Возвращает текущий размер пробной¦ ¦ ¦ области (последнее значение, уста-¦ ¦ ¦ новленное OvrSetRetry). ¦ +-------------------------+-------------------------------------+ ¦ OvtInit ¦ Эта процедура инициализирует подсис-¦ ¦ ¦ тему управления оверлеями и открыва-¦ ¦ ¦ ет оверлейный файл. ¦ +-------------------------+-------------------------------------+ ¦ OvrInitEMS ¦ Данная процедура, если это возможно,¦ ¦ ¦ загружает оверлейный файл в память¦ ¦ ¦ EMS. При этом все последующие заг-¦ ¦ ¦ рузки оверлеев сводятся к быстрой¦ ¦ ¦ передаче информации из памяти в па-¦ ¦ ¦ мять. ¦ +-------------------------+-------------------------------------+ ¦ OvrSetBuf ¦ Устанавливает размер оверлейного бу-¦ ¦ ¦ фера. ¦ +-------------------------+-------------------------------------+ ¦ OvrSetRetry ¦ Задает размер пробной области в¦ ¦ ¦ оверлейном буфере. ¦ L-------------------------+-------------------------------------> Константы и переменные модуля Overlay В модуле Overlay определены пять переменных: Переменные модуля Overlay Таблица 20.2 -----------------------T---------------------------------------- ¦ Переменная ¦ Описание ¦ +----------------------+----------------------------------------+ ¦ OvrFileMode ¦ Определяет передаваемый DOS при откры-¦ ¦ ¦ тии файла код доступа. ¦ +----------------------+----------------------------------------+ ¦ OvrLoadCount ¦ Данная переменная увеличивается при¦ ¦ ¦ каждой загрузке оверлея. ¦ +----------------------+----------------------------------------+ ¦ OvrReadBuf ¦ Эта процедурная переменная позволяет¦ ¦ ¦ вам интерпретировать операции загрузки¦ ¦ ¦ оверлея. ¦ +----------------------+----------------------------------------+ ¦ OvrResult ¦ Перед возвратом управления каждая про-¦ ¦ ¦ цедура в модуле Overlay сохраняет свой¦ ¦ ¦ код результата в переменной OvrResult. ¦ +----------------------+----------------------------------------+ ¦ OvrTrapCount ¦ Каждый раз, когда обращение к подпрог-¦ ¦ ¦ рамме оверлея перехватывается подсисте-¦ ¦ ¦ мой управления оверлеями (когда оверлея¦ ¦ ¦ нет в памяти или он находится на тести-¦ ¦ ¦ ровании) значение переменной¦ ¦ ¦ OvrTrapCount увеличивается. Начальное¦ ¦ ¦ ее значение равно 0. ¦ L----------------------+----------------------------------------- Значения этих переменных вы можете найти в Главе 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: const 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.Оверлеи в файлах .EXE
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));
Назад | Содержание | Вперед