Адресуемое пространство памяти в операционной системе MS-DOS организовано сегментами: последовательными блоками памяти по 64K каждый. Если известен сегмент, то дальнейшее уточнение места объекта в памяти происходит по его смещению, т.е. номеру байта от начала сегмента. Это, может быть, не самый эффективный способ адресации памяти, но на нем основана операционная система MS-DOS и все программы для нее. Таким образом, любая ячейка адресуемого пространства MS-DOS определяется парой чисел СЕГМЕНТ:СМЕЩЕНИЕ. При этом сегмент может начинаться с любого физического адреса, что порождает множественность способов адресации ячейки памяти. Например, такие разные адреса, как $83FD:$000B, $7FFD:$400B и $759D:$E60B в действительности адресуют к одной и той же ячейке. Иногда может быть интересно получить сплошной адрес ячейки, отсчитанный от начала памяти 0000:0000. Такое число получить очень просто: оно равно СЕГМЕНТ *16 + СМЕЩЕНИЕ.
Существует понятие нормализации адреса. Под этим понимается приведение его к такому виду, что смещение находится в диапазоне от 0 до 15 ($000F). Если вычислен сплошной адрес ячейки памяти, то его можно легко пересчитать в нормализованный «обычный» формат:
СЕГМЕНТ = Сплошной_адрес div 16
и
СМЕЩЕНИЕ = Сплошной_адрес mod 16,
где div и mod — операции деления нацело и взятие остатка от деления соответственно.
Сплошное представление адреса может быть очень большим числом, и мы рекомендуем использовать для его хранения тип LongInt.
- 187 -
10.2. Распределение памяти при выполнении программ
Рассмотрим распределение памяти для выполнимого кода программ на Турбо Паскале (рис. 10.1).
Рис. 10.1
При запуске программы (ЕХЕ-файла) MS-DOS организует в памяти нечто вроде анкеты длиной 256 байт на этот файл, называемой PSP (Program Segment Prefix). Структура PSP описывается в технических руководствах по MS-DOS. Сегмент, с которого начинается отсчет PSP, может быть получен через предопределенную в системной библиотеке (модуле System) переменную PrefixSeg типа Word.
После PSP начинается код ЕХЕ-файла. Он может занимать более одного сегмента. Когда выполняется какой-либо из блоков кода (основной блок, процедуры из модулей), он считается размещенным
- 188 -
в некотором сегменте кода. Реальное значение сегмента при этом содержится в регистре CS процессора.
Статические глобальные переменные основного блока и все типизированные константы, включая локальные, располагаются в сегменте данных, который запоминается регистром DS процессора. Общий объем переменных и типизированных констант не может в сумме превышать 64K.
Заметим, что реальный сегмент может быть и меньше чем 64K. В самом деле, он может начинаться где угодно. Если самый последний байт в нем имеет смещение, например 255 ($00FF), то следующий сегмент может отсчитываться от следующего же байта.
Следом за сегментом данных следует область стека. В ней располагаются локальные переменные и параметры-значения процедур и функций во время их работы по вызову. Сегмент стека содержится в регистре SS процессора. Турбо Паскаль отводит под стек один сегмент, и поэтому область стека не может превышать 64K.
Стек заполняется от своей верхней границы (она может быть назначена директивой компилятору $М) по направлению к началу, т.е. к старту сегмента. Специальный регистр SP процессора содержит смещение указателя стека в сегменте SS (указатель стека — это как бы отметка уровня заполнения стека).
Имеется предопределенная системная переменная StackLimit типа Word, которая логически примыкает к рассматриваемым вопросам. Она содержит минимальное допустимое значение указателя стека. Когда программа запускается, указатель стека имеет максимальное значение, равное отведенной под стек памяти. При работе стек заполняется «сверху вниз», и указатель как бы снижается. Как только он спустится настолько, что свободная часть стека станет меньше чем значение StackLimit, возникнет ошибка времени счета номер 202 Stack overflow (переполнение стека), и программа прервется.
Нормальное значение StackLimit — это 0, а в режиме компиляции {$N+, E+} — 224. Обычно нет необходимости менять это значение.
Выше стека программа отводит себе память под буфер для работы оверлеев — перекрывающихся частей программы. Если они не используются, то буфер не отводится (подробнее об этом см. гл. 18 «Модуль Overlay»).
Еще выше располагается область памяти для размещения динамических переменных и структур данных, называемая областью кучи или просто кучей. Подробное строение кучи будет описано в разд. 11.3.
- 189 -
10.3. Анализ расположения кода и областей данных программы
При программировании на низком уровне или использовании вставок машинных кодов в программу необходимо иметь средства анализа положения программы в ее данных в оперативной памяти. Системная библиотека Турбо Паскаля содержит набор средств для этого. Перечень специальных функций анализа памяти приведен в табл. 10.1.
Таблица 10.1
Функция : Тип -- Возвращаемое значение