Другие возхможности Турбо Паскаля
Внешние процедуры (функции)
С помощью внешних процедур (функций) можно осуществить вызов из программы процедур или функций, написанных на языке ассемблера. Ассемблер обеспечивает компиляцию программ, написанных на машинно-ориентированном языке программирования низкого уровня. В Турбо Паскале есть собственный встроенный ассемблер см. гл.12). В этом разделе речь идет о программах, написанных и откомпилированных с помощью внешнего ассемблера, такого как, например, ассемблер фирмы MicroSoft или Turbo Assembler фирмы Borland.
Машинно-ориентированный язык ассемблера предоставляет квалифицированному программисту богатейшие возможности использования всех особенностей архитектуры ПК. Ассемблерные программы выполняются значительно быстрее и занимают меньший объем памяти, чем программы, написанные на Турбо Паскале, однако низкий уровень языка ассемблера существенно снижает производительность труда программиста и резко усложняет отладку программ. Как правило, на языке ассемблера пишутся сравнительно небольшие фрагменты программ, в которых используются недоступные из Турбо Паскаля особенности архитектуры ПК.
Внешняя процедура (функция) в программе, написанной на Турбо Паскале, объявляется своим заголовком, за которым следует стандартная директива EXTERNAL, например:
Function LoCase (ch : char):char; external; 
Procedure Swapping (var a,b; N:word); external;
Как видно из этих примеров, тело внешней процедуры (функции) отсутствует - его заменяет директива EXTERNAL. Для подключения ассемблерной программы необходимо предварительно ее откомпилировать и получить объектный файл с расширением OBJ, содержащий перемещаемый код ассемблерной программы. Непосредственно перед описанием внешней процедуры (функции) в тело основной программы вставляется директива компилятора {$L<имя файла>}, где <имя фата> - имя OBJ-файла. Диск и каталог, в котором следует искать этот файл, если он не обнаружен в текущем каталоге, указываются опцией OPTIONS/DIRECTORIES/OBJECT DIRECTORIES (см.прил.1).
Перед передачей управления внешней процедуре (функции) программа помещает параметры обращения в программный стек в том порядке, как они перечислены в заголовке процедуры (функции). Ассемблерная процедура должна сохранить регистры ВР, SP, SS и DS центрального процессора в самом начале своей работы и восстановить содержимое этих регистров перед возвратом управления в программу. Остальные регистры можно не сохранять и соответственно не восстанавливать.
Параметры могут передаваться по ссылке или по значению. Если параметр передается по ссылке, в стек помещается указатель, содержащий абсолютный адрес параметра, если по значению - в стек помещается сам параметр, точнее - его значение. Все параметры-переменные, т.е. параметры, объявленные в заголовке с предшествующим словом VAR, всегда передаются по ссылке. Параметры-значения могут передаваться по ссылке или по значению в зависимости от длины внутреннего представления соответствующего параметра. В общем случае используется следующее правило: если длина внутреннего представления параметра-значения составляет 1, 2 или 4 байта, он передается своим значением, т.е. его значение помещается в стек. Точно так же через стек передаются и все вещественные данные длиной в 4, 6, 8 и 10 байт (в версии 4.0 эти данные передаются через стек сопроцессора 8087/80287, начиная с версии 5.0 -через стек центрального процессора 8086/80486). Во всех остальных случаях, если длина внутреннего представления больше 4 байт, соответствующий параметр передается по ссылке.
Ассемблерные функции в зависимости от длины внутреннего представления результата должны возвращать его через регистры центрального процессора или сопроцессора по следующим правилам:

  • длиной в 1 байт - в регистре AL;
  • длиной в 2 байта - в регистре АХ;
  • длиной в 4 байта - в регистрах DX:AX (старшее слово в DX);
  • тип REAL (6 байт) - в регистрах DX:BX:AX;
  • типы SINGLE, DOUBLE, EXTENDED и СОМР - через стек сопроцессора 8087/80486;
  • указатели - в регистрах DX:AX (сегмент в DX);
  • строки возвращаются по ссылке: адрес начала строки помещается в DX:AX (сегмент в DX).

Все ассемблерные процедуры должны размещаться в сегменте с именем CODE или CSEG, или с именем, оканчивающимся на _ТЕХТ; инициализированные локальные Переменные помещаются в сегмент с именем CONST или с именем, оканчивающимся на _DATA. Все другие локальные переменные необходимо размещать в сегменте с именем DATA или DSEG, или с именем, оканчивающимся на _BSS. Любые другие объявления сегментов игнорируются. Все имена, объявленные в интерфейсной части модулей программы, написанной на Турбо Паскале, становятся доступны ассемблерной процедуре (функции) после их объявления директивой EXTRN. Точно так же все имена ассемблерных процедур и функций, которые должны быть доступны программе на Турбо Паскале, следует объявлять директивой PUBLIC.
 
Использование встроенных машин кодов
В Турбо Паскале имеется возможность непосредственного включения в программу небольших фрагментов, написанных в машинных кодах. Для этого используется стандартная директива INLINE, за которой в круглых скобках следует один или несколько элементов машинного кода, разделяемых косыми чертами. Элемент кода, в свою очередь, строится из одного или более элементов данных, разделенных знаками «+» или «-»
В качестве элемента данных может использоваться целая константа, идентификатор (переменной, константы или функции) или ссылка на счетчик адреса («*»). Каждый элемент данных вызывает генерацию 1 или 2 байт кода программы. Значение этого кода получается сложением или вычитанием элементов данных в соответствии с разделяющим их знаком. Значением идентификатора переменной, константы, функции служит адрес соответствующего объекта, значением ссылки на счетчик адреса является тот адрес, по которому будет размещаться следующий байт кода.
Элемент кода будет генерировать 1 байт кода, если этот элемент состоит только из целых констант и значение результата не превышает мощности одного байта, т.е. находится в диапазоне от 0 до 255. Если значение превышает 255 или элемент кода содержит ссылку на счетчик адреса, генерируются 2 байта. Знаки «<» и «>» могут использоваться -для отмены автоматического выбора размера генерируемого кода. Если элемент кода начинается со знака «<», в код заносится только 1 байт (с младшей значимостью), даже если само значение занимает 2 байта. Наоборот, если элемент начинается со знака «>», в код заносятся 2 байта (старший байт может оказаться нулевым).
Значением идентификатора является смещение соответствующего объекта. Если переменная - глобальная, смещение задается относительно сегмента данных, хранящееся в регистре DS, если это локальная переменная, - относительно сегмента стека регистр SP). Базовым сегментом типизированной константы является сегмент кода регистр CS).
В следующем примере приводятся две короткие процедуры, с помощью которых можно ввести или вывести данные через любой порт ПК.
Function InPort(Port: Word): Word; 
var
pp: Word;
cc:Char; 
egin
pp:=port; 
inline (
$8b/$96/pp/ { mov DX,pp[bp] }
$EC/ { IN AX,DX }
$88/$86/cc); {mov cc[bp],AX}
InPort:=ord(cc); 
end;
Procedure OutPort(Port,Bt: Word); 
var
pp: Word;
cc:Char; 
begin
pp:=port;
cc:=chr(Bt);
inline (
$8a/$86/cc/ { mov AX,cc[bp] }
$8b/$96/pp/ { mov DX,pp[bp] }
$EE) { OUT DX,AX } 
end;
Операторы INLINE могут произвольным образом смешиваться с другими операторами Турбо Паскаля, однако при выходе из процедуры (функции) содержимое регистров ВР, SP, DS и SS должно быть таким же, как и при входе в нее.
С помощью директивы INLINE можно также задавать последовательность машинных кодов, которую необходимо несколько раз вставить в программу. Для этого используется описание INLINE-процедуры, например:
Procedure DisableInterrupts; 
inline ($FA); {CLI}
INLINE-процедура имеет обычный для Турбо Паскаля заголовок, в то время как тело процедуры пишется целиком с помощью оператора INLINE. Всякий раз, когда в программе будет встречаться оператор вызова INLINE-процедуры, компилятор Турбо Паскаля будет вставлять на это место не код вызова процедуры, а нужные машинные коды. Например, вместо вызова процедуры в операторе
DisableInterrupt;
компилятор вставит команду запрета прерываний CLI. Таким образом, INLINE-процедуры служат своеобразным средством расширения возможностей стандартного компилятора Турбо Паскаля и подобны макросам ассемблера. Использование INLINE-процедур увеличивает скорость исполнения программы, так как не осуществляется генерация (и исполнение) команд передачи управления в процедуру. По этой причине в INLINE-процедурах не следует использовать команды выхода из подпрограммы. INLINЕ-процедура может иметь параметры, однако на них нельзя ссылаться в INLINE-директивах (на другие символы Турбо Паскаля ссьшаться можно). В следующем примере перемножаются два числа типа INTEGER, результат имеет тип LONGINT:
FUNCTION LongMul(X,YInteger) : Longint;
inline ( 
$5 А/ {POP AX; получить в АХ число Х }
$58/ { POP DX; получить в DX число Y } 
$F7/$EA); { IMUL DX; DX:AX ;= X * Y }
Отметим, что в силу упоминавшегося сходства с макросами ассемблера, имена INLINE-подпрограмм не могут использоваться в качестве аргументов в операторах @ или служить параметрами функций ADDR, OFS и SEG.
 
Обращения к функциям операционной системы
Турбо Паскаль предоставляет программисту практически неограниченные возможности использования любых функций стандартной операционной системы MS-DOS. При внимательном анализе материала этой книги Вы, очевидно, заметите, что значительную его часть составляет описание многочисленных библиотечных процедур и функций. Собственно язык Паскаль весьма прост и лаконичен, что, по мнению многих специалистов, и послужило одной из причин его широкого распространения. Библиотечные же процедуры и функции, в своей значительной части, являются, по существу, своеобразным интерфейсом между языковыми средствами Турбо Паскаля и функциями операционной системы. Разумеется, можно только приветствовать усилия разработчиков Турбо Паскаля по созданию мощных библиотек TURBO.TPL и GRAPH.TPU, однако ясно, что таким способом невозможно запрограммировать все допустимые обращения к средствам ДОС. Вот почему в Турбо Паскаль включены две процедуры, с помощью которых программист может сам сформировать вызов той или иной функции дисковой операционной системы (ДОС).
Следует учесть, что единственным механизмом обращения к функциям ДОС является инициация программного прерывания. Прерывание - это особое состояние вычислительного процесса. В момент прерывания нарушается нормальный порядок выполнения команд программы и управление передается специальной процедуре, которая входит в состав ДОС и называется процедурой обработки прерывания. Каждое прерывание характеризуется в рамках ДОС порядковым номером и связано со своей процедурой обработки. В архитектуре центрального процессора ПК предусмотрены прерывания двух типов - аппаратные и программные. Аппаратные прерывания создаются схемами контроля и управления ПК и сигнализируют операционной системе о переходе какого-либо устройства в новое состояние или о возникновении неисправности. Программные прерывания инициируются при выполнении одной из двух специальных команд микропроцессора (INT или INTO) и служат для обращения к средствам ДОС.
Описываемые ниже процедуры входят в состав библиотечного модуля DOS.TPU и становятся доступными после объявления USES DOS. При возникновении программного прерывания в большинстве случаев необходимо передать процедуре обработки прерывания некоторые параметры, в которых конкретизируется запрос нужной функции. Эти параметры, а также выходная информация (результат обработки прерывания) передаются от программы к процедуре и обратно через регистры центрального процессора. В составе модуля DOS.TPU для этих целей определен специальный тип:
type
Registers = record case integer of
0 : (AX, BX, CX, BP, SI, DI, DS, ES, Flags : word); 
1 : (AL, AH, BL, BH, CL, CH, DL, DH : byte) 
end ;
Этот тип имитирует регистры центрального процессора и дает возможность обращаться к ним как к 16-битным или 8-битным регистрам.
Процедура INTR. С помощью этой процедуры инициируется программное прерывание с требуемым номером. Обращение:
INTR (<N>,<регистры>)
Здесь <N> - выражение типа BYTE; номер прерывания;
<регистры> - переменная типа REGISTERS; в этой переменной процедуре обработки прерывания передается содержимое регистров и в ней же возвращается выходная информация.
Например, прерывание с номером 18 ($12) возвращает в регистре АХ объем оперативной памяти ПК. Короткая программа, представленная в примере 11.1, выведет на экран сообщение об этом объеме.
Пример 11.1. 
Uses DOS; 
var
r : registers; 
begin
Intr ($12, r); 
writeln ('Объем памяти = ',r.AX, ' Кбайт')
end.
Процедура MSDOS. Инициирует прерывание с номером 33 ($21). Формат обращения:
MSDOS (<регистры>)
Программное прерывание с номером 33 ($21) стоит особняком, так как оно дает доступ к большому количеству функций ДОС (этим прерыванием вызывается 85 функций). Рассматриваемая процедура полностью эквивалентна вызову процедуры INTR с номером прерывания 33. Например, следующая программа (пример 11.2) выведет на экран версию операционной системы:
Пример 11.2 
Uses DOS; 
var
r : registers; 
begin
r.АН := $30;
MsDos(r);
WriteLn('Версия операционной системы: ', r.AL, '.' r.АН) 
end.
 
Поддержка процедур обработки прерываний
При написании процедур обработки прерываний существенными являются два обстоятельства. Во-первых, процедура обработки прерывания не должна искажать работу прерванной программы. Для этого необходимо сначала сохранить регистры центрального процессора, а перед выходом из процедуры - восстановить их. Во-вторых, процедура должна строиться по принципу реентерабельности (повторной входимости): ее работа может быть прервана в любой момент другими прерываниями и ДОС может обратиться к соответствующей функции до завершения обработки предыдущего прерывания.
Турбо Паскаль предоставляет программисту возможность написания процедур обработки прерывания на языке высокого уровня, хотя обычно такие процедуры пишутся на языке ассемблера.
Процедура обработки прерывания, написанная на Турбо Паскале, должна начинаться стандартной директивой INTERRUPT (прерывание), например:
Procedure IntProc (Flags, CS, IP, AX, BX, CX, DX,
SI, DF, DS, ES, BP : word); inerrupt; 
begin
...
end ;
Формальные параметры в заголовке процедуры должны перечисляться в указанном порядке - через эти параметры все регистры прерванной программы становятся доступны процедуре обработки прерывания. Количество перечисляемых в заголовке процедуры параметров-регистров может быть любым, но не больше 12. Если в списке опущен какой-либо параметр, должны быть опущены также и все предшествующие ему параметры. Например, описание
Procedure IntProc(SI, DP, ES: word); interrupt; 
будет неверным (опущены параметры DS и ВР); правильное описание:
Procedure IntProc(SI, DP, DS, ES, BP: word); interrupt;
Заметим, что компилятор не контролирует порядок перечисления параметров в заголовке процедуры обработки прерывания.
Директива INTERRUPT вызывает генерацию специальных машинных кодов, обеспечивающих заталкивание регистров в стек при входе в процедуру и извлечение их из стека перед выходом из нее.
При входе в процедуру:


push

ax

 

push

bx

 

push

cx

 

push

dx

 

push

si

 

push

di

 

push

ds

 

push

es

 

push

bp

 

mov

bp,

si

sub

sp,

LocalSize

mov

ax,

SEG DATA

mov

ds,

ax

При выходе из процедуры:
mov sp, bp
pop bp
pop es
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
irep
В самой процедуре обработки прерывания не рекомендуется обращаться к другим функциям ДОС, так как некоторые из них, в том числе все функции ввода-вывода, нереентерабельны.
Для связи с любыми процедурами прерываний, а следовательно, и с процедурами, написанными программистом, используются векторы прерываний - четырехбайтные абсолютные адреса точек входа в эти процедуры. Векторы прерываний располагаются в младших адресах оперативной памяти, начиная с нулевого адреса: прерывание номер 0 - по адресу 0, номер 1 - по адресу 1*4 = 4, номер N - по адресу N * 4. С помощью следующих двух процедур программист может прочитать содержимое любого вектора или установить его новое значение.
Процедура GETINTVEC.
Возвращает вектор прерывания с указанным номером. Обращение:
GETINTVEC (<,<вектор>)
Здесь <D> - выражение типа BYTE; номер прерывания;
<вектор> - переменная типа POINTER; адрес точки входа в процедуру обработки прерывания.
Представленная в примере 11.3 программа выводит на экран содержимое всех ненулевых векторов прерываний.
Пример 11.3
Uses DOS; 
var
i : byte; p : pointer; 
begin
for i := 0 to 255 do 
begin
GetlntVec (i, p) ;
if (Seg (р) <> 0) or (Ofs (рл) <> 0) then 
WriteLn (' N=', i:3, ' Seg=', Seg (р):5,
' Ofs =' , Ofs (р) :5) 
end 
end.
Процедура SETINTVEC.
Устанавливает ндвое значение вектора прерывания. Формат обращения:
SETINTVEC (<,<адрес>)
Здесь <D> - выражение типа BYTE; номер прерывания;
<адрес> - выражение типа POINTER; адрес точки входа в процедуру обработки прерывания.
При нормальном завершении программы она выгружается из памяти, что делает невозможным разработку резидентных в памяти процедур обработки прерываний. Вы можете прекратить работу программы и оставить ее резидентной в памяти, если воспользуетесь процедурой KEEP.
Процедура KEEP.
Завершает работу программы и оставляет ее резидентной в памяти. Обращение:
KEEP (<код>)
Здесь <код> - выражение типа WORD - код завершения программы. Код завершения представляет собой фактически единственный механизм передачи сообщений от запущенной программы к программе, которая ее запустила. Он может быть проанализирован в вызывающей программе с помощью функции DOSEXITCODE.
Функция DOSEXITCODE.
Возвращает значение типа WORD - код завершения подчиненной программы. Обращение:
DOSEXITCODE
  
Запуск внешних программ
Из программы, написанной на Турбо Паскале, можно запустить любую другую готовую к работе программу. Для этого используется процедура ЕХЕС из библиотечного модуля DOS. Формат обращения к процедуре:
ЕХЕС (<имя>,<параметры>)
Здесь <имя> - выражение типа STRING; имя файла с вызываемой программой; <параметры> - выражение типа STRING; параметры вызова.
Имени запускаемой программы может предшествовать путь к файлу. Параметры передаются запускаемой программе в виде текстовой строки и могут быть проанализированы ею с помощью двух следующих функций.
Функция PARAMCOUNT.
Возвращает общее количество параметров вызова программы (значение типа WORD). Обращение:
PARAMCOUNT
Параметры вызова обычно следуют в командной строке ДОС сразу за именем вызываемой программы и отделяются от этого имени и друг от друга пробелами, например:
C:\TP\TURBO MYPROG.PAS 
C:\SIAM A:\SYSTEM1.SIA
Здесь MYPROG.PAS и A:\SYSTEM1.SIA - параметры, передаваемые программам TURBO и SIAM.
При вызове программы непосредственно из среды Турбо Паскаля ей можно передать параметры с помощью опции OPTIONS/PARAMETERS (см. прил.1).
Функция PARAMSTR.
Возвращает значение типа STRING, соответствующее нужному параметру вызова. Формат обращения:
PARAMSTR (<D>)
Здесь <D> - выражение типа WORD; порядковый номер параметра.
Заметим, что программе всегда передается параметр, соответствующий N = 0. В этом параметре ДОС сообщает полное имя запущенной программы с указанием диска и каталога, откуда она была загружена.
Использование процедуры ЕХЕС имеет ряд особенностей. Прежде всего необходимо отметить, что сама вызывающая программа остается резидентной в памяти, поэтому она не должна занимать всю оперативную память. Объем выделяемой программе памяти регулируется опцией OPTIONS/MEMORY SIZES (см. прил.1). По умолчанию параметры LOW HEAP LIMIT и HIGH HEAP LIMIT этой опции таковы (соответственно 0 и 655360 байт), что вызывающая программа, написанная на Турбо Паскале, занимает весь доступный объем памяти, и вызываемая программа не будет загружена. Полезно включить в текст вызывающей программы директиву компилятора, в которой изменяются принятые по умолчанию размеры памяти. Например, так:
{$М 2048, 0, 0}
Такая директива ограничивает используемую программой область стека величиной 2 Кбайта и исключает возможность использования в ней динамической памяти. Разумеется, Вы можете установить и другие значения параметров в этой директиве.
Специфические особенности исполнения программ Турбо Паскаля требуют изменения стандартных значений некоторых векторов прерываний. К ним относятся векторы со следующими шестнадцатеричными номерами:
$00, $02, $18, $23, $24, $34, $35, $36, $37, 
$38, $39, $ЗА, $ЗВ, $ЗС, $3D, $3E, $3F, $75.
Начальные значения этих векторов сохраняются в восемнадцати переменных с именами SA VEINTXX из библиотечного модуля SYSTEM, где XX - шестнадцатеричный номер прерывания. Поэтому непосредственно перед запуском внешней программы и сразу после возврата из нее рекомендуется вызывать библиотечную процедуру без параметров SWAPVECTORS, которая обменивает содержимое векторов прерывания и перечисленных переменных.
Программа из примера 11.4 читает с клавиатуры любую команду ДОС, затем вызывает командный процессор COMMAND. COM операционной системы и передает ему эту команду.
Обратите внимание: для указания файла COMMAND.COM и пути к нему используется обращение к библиотечной функции GETENV, с помощью которой можно получить параметры настройки операционной системы. В частности, параметр COMSPEC определяет спецификацию файла, содержащего командный процессор.
Пример 11.4 
{$М 1024, 0, 0} 
Uses DOS; 
var
st : string [79]; 
begin
write ('Введите команду ДОС: '); 
readln (st); if st <> '' then 
begin
st := '/C '+st; 
SwapVectors;
Exec (GetEnv ('COMSPEC'), st) ; 
SwapVectors 
end 
end.
Функция ENVCOUNT.
Возвращает значение типа INTEGER, в котором содержится общее количество установленных в ДОС параметров. Обращение:
ENVCOUNT
Функция ENVSTR.
Возвращает значение типа STRING, содержащее имя и значение нужного параметра настройки операционной системы. Формат обращения:
ENVSTR (<D>)
Здесь <D> - выражение типа INTEGER; номер параметра.
Эта функция возвращает строку типа NAME-VALUE, где NAME - имя, a VALUE -значение соответствующего параметра настройки.
Функция GETENV.
Возвращает значение типа STRING, в котором содержится параметр настройки ДОС. Формат обращения:
GETENV (<имя>)
Здесь <имя> - выражение типа STRING; имя параметра.
Эта функция имеет параметр обращения NAME, а возвращает значение VALUE (см. функцию ENVSTR).
 
Оверлей
Как отмечалось в гл.9, максимальный размер модуля не может превышать 64 Кбайта, однако количество модулей не ограничено, что дает возможность разрабатывать весьма крупные программы, занимающие, например, всю доступную оперативную память ПК (приблизительно 580 Кбайт). Тем не менее, в некоторых случаях и этот объем может оказаться недостаточным. Турбо Паскаль предоставляет в распоряжение программиста простой и достаточно эффективный механизм оверлея, с помощью которого можно создавать программы практически неограниченной длины (следует оговориться, что речь идет только о длине кода программы; два важных размера -длина сегмента данных и размер программного стека - в Турбо Паскале не могут превышать 64 Кбайта независимо от структуры программы).
Оверлей - это такой способ использования оперативной памяти, при котором в один и тот же участок памяти, называемый оверлейным буфером, попеременно по мере надобности загружаются различные оверлейные (перекрывающиеся) модули. При этом все оверлейные модули в готовом к работе виде хранятся на диске, а в оперативной памяти в каждый момент находится лишь один активный модуль и, возможно, небольшое число неактивных.
Пусть, например, программа ( 1) состоит из главной части MAIN и двух модулей А к В, a LM, LA и LB - соответственно длина главной части и обоих модулей, причем LA > LB. Тогда неоверлейная программа займет в памяти LM + LA + LB байт, в то время как- оверлейная программа - лишь LM + LA байт.
При исполнении оверлейной программы в память первоначально загружается главная часть и один из модулей, например, модуль А. Если в процессе исполнения программы встретится обращение к модулю В, программа приостановит свою работу, с диска в оверлейный буфер будет загружен модуль В (модуль А при этом частично уничтожается), после чего программа продолжит свою работу. Если в дальнейшем встретится обращение к А, точно таким же образом будет загружен модуль А, причем загрузка нужных модулей в оверлейный буфер осуществляется автоматически и программисту не нужно об этом заботиться.
Описанный механизм выявляет главное преимущество оверлейной структуры: объем оперативной памяти, занимаемой оверлейной программой, определяется длиной ее главной части и наибольшего из перекрывающихся модулей, в то время как при неоверлейной структуре в этот объем входит суммарная длина всех модулей. Чем больше в программе оверлейных модулей и чем меньше длина наибольшего из них, тем больший выигрыш в памяти дает оверлейная структура. Однако совершенно очевиден и главный недостаток таких структур: на каждую-загрузку оверлейного модуля с диска в оверлейный буфер требуется дополнительное время, поэтому оверлейная программа будет исполняться с меньшей скоростью.
Работа оверлейных программ обеспечивается с помощью процедур и функций библиотечного модуля OVERLAY, входящего в библиотечный файл TURBO.TPL.
При создании оверлейных программ нужно руководствоваться следующей последовательностью действий.

  • Выделить главную часть программы и разбить оставшуюся часть на несколько модулей. Отметим, что никаких дополнительных ограничений на модули, по сравнению с описанными в гл.9, не накладывается за одним исключением: в оверлейных модулях нельзя использовать процедуры обработки прерываний. Желательно продумать состав модулей таким образом, чтобы по возможности минимизировать количество их перезагрузок в оверлейный буфер в процессе исполнения программы.
  • В главной части программы указать с помощью директив компилятора вида {$О <имя>} те модули, которые будут оверлейными, например:

Program Main; 
Uses CRT, DOS,Graph, Overlay, UnitA, UnitB;
{$0 DOS} 
{$0 UnitA} 
{$O UnitB}
Следует подчеркнуть, что из всех стандартных библиотечных модулей только один модуль DOS может быть оверлейным, остальные модули (CRT, Graph, Printer и т.д.) не могут объявляться оверлейными.

  • Предусмотреть перед первым по логике работы программы обращением к оверлейному модулю вызов процедуры инициализации оверлея OVRINIT. Здесь же, если это необходимо, следует установить размер оверлейного буфера и указать возможность использования расширенной памяти (см. ниже).
  • В начале главной программы и каждого оверлейного модуля необходимо поместить директивы компилятора {$О+} и {$F+} или установить опции OPTIONS /COMPILE/FORCE FAR CALLS и OPTIONS/ COMPILE/ OVERLAYS ALLOWED (см. прил.1) в активное состояние, после чего откомпилировать программу на диск. Программа готова к работе.

Таким образом, все процедуры и функции в оверлейной программе должны использовать дальнюю модель вызова - это обязательное условие. Отметим, что попытка компиляции оверлейного модуля, в начале которого отсутствует директива {$О+} предполагается, что опция среды OPTIONS/COMPILE/OVERLAY ALLOWED неактивна), будет обнаружена компилятором, в то время как! неправильная (ближняя) модель вызова оверлейных подпрограмм компилятором не контролируется и может привести к непредсказуемым результатам при исполнении программы.
Далее, инициация оверлея (осуществляется вызовом процедуры OVRINIT, см. ниже) должна происходить до вызова любого из оверлейных модулей. Это требование кажется тривиальным, однако множество проблем в оверлейных программах обычно :вязано именно с ним. Дело в том, что обращение к оверлейному модулю может происходить еще до начала работы основной программы: напомню, что любой модуль (в том числе и оверлейный) может иметь инициирующую часть, которая исполняется перед началом работы основной программы. В связи с этим рекомендую придерживаться следующего простого правила: никогда не используйте оператор BEGIN в конце модуля, если Вам нет нужды в инициирующих действиях; пустая инициирующая часть содержит пустой оператор, которому будет передано управление на этапе инициации. Таким образом, пустая инициирующая часть оверлейного модуля очень часто может вызывать сообщение об ошибке периода исполнения с кодом 208 (не установлена система управления оверлеем). Как же быть, если в оверлейном модуле все-таки нужна инициирующая часть? В этом случае можно рекомендовать следующий прием. Создайте лишний модуль, в котором будут пустыми все части, кроме инициирующей. В этой части разместите команды инициации оверлея. Новый модуль не должен быть оверлейным и его имя должно стоять в предложении USES основной программы перед именем любого оверлейного модуля. После компиляции такой программы инициация оверлея будет осуществляться перед выполнением любой другой инициирующей части и проблема будет решена.
Процедура OVRINIT.
Инициализирует оверлейный файл. Обращение:
OVRINIT (<имя>)
Здесь <имя> - выражение типа STRING; имя файла с оверлейной частью программы.
При компиляции оверлейной программы создается специальный файл с именем, совпадающим с именем главной программы, и расширением .OVR. В этот файл компилятор помещает все оверлейные модули, из него же эти модули загружаются в оверлейный буфер в процессе исполнения программы. Файл с оверлейной частью программы должен размещаться в том же каталоге, что и файл с главной частью (с расширением .ЕХЕ). Отметим, что имя оверлейного файла необходимо дополнять расширением .OVR.
Обычно размер оверлейного буфера определяется автоматически таким образом, чтобы в нем мог разместиться самый крупный из всех оверлейных модулей. Программист может увеличить размер буфера. Тогда при загрузке в буфер очередного модуля программа проверит, достаточно ли в буфере свободного места и, если достаточно, загрузит новый модуль сразу за старым, который, таким образом, не будет уничтожен. Такой механизм способствует минимизации потерь времени на перезагрузку модулей. Если установлен очень большой размер буфера, то в нем, возможно, смогут разместиться все оверлейные модули, однако в этом случае оверлейная структура становится просто ненужной.
Процедура OVRSETBUF.
Устанавливает размер оверлейного буфера. Формат обращения:

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика