Справочник программиста на персональном компьютере фирмы IBM. Системные ресурсы
Страница 15. Манипуляции с памятью


 

1.3.1 Манипуляции с памятью.

Когда MS DOS загружает программу, то она помещается в младшую
область памяти, сразу же за COMMAND.COM и установленными драйве-
рами устройств или другими утилитами, которые резидентны в памя-
ти. В этот момент времени вся память за программой отведена этой
программе. Если программе нужна память для создания области дан-
ных, то она может приближенно вычислить где в памяти кончается ее
код и затем поместить требуемую область данных в любое место за
концом кода. Для определения адреса конца программы поместите в
конце программы псевдосегмент типа:
ZSEG SEGMENT
;
ZSEG ENDS
В ассемблере IBM PC ZSEG будет последним сегментом, так как
сегменты располагаются в алфавитном порядке. С другими ассембле-
рами нужно действительно поместить эти строки в конце программы.
В самой программе достаточно поставить оператор MOV AX,ZSEG и AX
будет указывать на первый свободный сегмент памяти за программой.
Такой подход будет работать до тех пор, пока программа не
будет предполагать о наличии памяти, которой на самом деле нет.
Он не будет также работать в многопользовательской среде, когда
несколько программ могут делить между собой одну и ту же область
адресов. Для решения этой проблемы MS DOS имеет возможность отс-
леживать 640K системной памяти и отводить по требованию программы
блоки памяти любого размера. Блок памяти - это просто непрерывная
область памяти, его максимальный размер определяется размером
доступной памяти, в частности, он может быть больше одного сег-
мента (64K). Если затребован слишком большой блок, то DOS выдает
сообщение об ошибке. Любая возможность перекрытия блоков исключе-
на. Кроме того MS DOS может освобождать, урезать или расширять
существующие блоки. Хотя программа не обязана использовать эти
средства, но удобно и предусмотрительно делать это. Некоторые
функции DOS требуют, чтобы были использованы средства управления
памятью DOS, например, завершение резидентной программы [1.3.4]
или вызов другой программы из данной [1.3.2].
Прежде чем отвести память, существующий блок (вся память от
начала программы до конца) должен быть обрезан до размера прог-
раммы. Затем, при создании блока, DOS создает 16-байтный управ-
ляющий блок памяти, который расположен непосредственно перед
блоком памяти. Первые 5 байтов этого блока имеют следующее значе-
ние:
байт 0 ASCII 90 - если последний блок в цепочке, иначе
ASCII 77.
байты 1-2 0 если блок освобожден
байты 3-4 размер блока в 16-байтных параграфах
DOS обращается к блокам по цепочке. Адрес первого блока хра-
нится во внутренней переменной. Значение этой переменной позво-
ляет DOS определить положение первого отведенного блока, а из
информации, содержащейся в нем, может быть найден следующий блок
и т.д., как показано на рис. 1-4. Как только Вы начали использо-
вать систему распределения памяти DOS, то Вы обязаны придержи-
ваться ее. Если программа изменит содержимое управляющего блока,
то цепочка будет разорвана и DOS начнет выдавать сообщения об
ошибке.
MS DOS обеспечивает три функции распределения памяти, номера
от 48H до 4AH прерывания 21H. Функция 48H отводит блок памяти, а
49H - освобождает блок памяти. Третья функция ("SETBLOCK") ме-
няет размер памяти, отведенной для программы; эта функция должна
быть использована перед двумя остальными. После ее выполнения
можно спокойно отводить и освобождать блоки памяти. Программа
должна освободить все отведенные ею блоки перед завершением.
Иначе эта память будет недоступной для последующего использова-
ния.

Средний уровень.

Все три функции распределения памяти прерывания 21H используют
16-битный адрес начала блока памяти, с которым они оперируют.
Этот адрес соответствует сегменту, с которого начинается блок
(блок всегда начинается со смещения 0 данного сегмента). Таким
образом реальный адрес ячейки начала блока равен этому адресу,
умноженному на 16. Также, для всех трех функций, BX содержит
число 16-байтных разделов памяти (параграфов), которые будут
отводиться или освобождаться. Если функция не может быть выполне-
на, то устанавливается флаг переноса, а в AX возвращается код
ошибки, объясняющий причину. Возможны три кода ошибки:
7 разрушен управляющий блок памяти
8 недостаточно памяти для выполнения функции
9 неверный адрес блока памяти
Функция отведения блока использует коды 7 и 8, а освобождения - 7
и 9, в то время как функция изменения блока использует все три
кода. В следующем примере сначала отводится блок, размером 1024
байта. При этом BX содержит требуемое число 16-байтных парагра-
фов, а при завершении стартовый адрес блока равен AX:0 (т.е.
смещение 0 в сегменте со значением, содержащимся в AX). Вторая
часть примера освобождает этот же блок, как и требуется при за-
вершении программы. В данном случае значение полученное в AX
помещается в ES. DOS следит за размером блока и знает какое коли-
чество параграфов надо освободить.
;---отведение блока размером 1024 байта
MOV AH,48H ;номер функции
MOV BX,64 ;требуем 64 параграфа
INT 21H ;пытаемся отвести блок
JC ERROR ;обрабатываем ошибку в случае неудачи
MOV BLOCK_SEG,AX;иначе сохраняем адрес блока
.
;---освобождаем тот же блок
MOV AX,BLOCK_SEG ;получаем стартовый адрес блока
MOV ES,AX ;помещаем его в ES
MOV AH,49H ;номер требуемой функции
INT 21H ;освобождаем блок памяти
Наконец, приведем пример использования функции 4AH. ES содер-
жит значение сегмента PSP, т.е. самого первого байта памяти, с
которого загружена программа. Это значение присваивается ES при
старте задачи. Для использования SETBLOCK надо либо вызывать эту
функцию в самом начале программы (прежде чем ES будет изменен),
либо сохранить его начальное значение для последующего использо-
вания.
BX содержит требуемый размер блока в 16-байтных параграфах.
Для определения этого размера поместите добавочный "искуственный"
сегмент в конец программы. В макроасссемблере IBM PC сегменты
располагаются в алфавитном порядке, поэтому Вы можете поместить
его в любое место программы, при условии, что его имя это что-то
вроде "ZSEG". В других ассемблерах действительно помещайте фик-
тивный сегмент в конец программы. Программа может прочитать пози-
цию этого сегмента и, сравнивая ее со стартовым сегментом, полу-
чить количество памяти, требуемое самой программе. В момент заг-
рузки программы и ES и DS содержат номер параграфа самого начала
программы в префиксе программного сегмента; для COM файлов CS
также указывает на эту позицию, но для EXE файлов это не так.
;---освобождение памяти (ES имеет значение при старте)
MOV BX,ZSEG ;получаем # параграфа конца программы + 1
MOV AX,ES ;получаем # параграфа начала программы
SUB BX,AX ;вычисляем размер программы в параграфах
MOV AH,4AH ;номер функции
INT 21H ;освобождаем память
JC MEMORY_ERROR ;проверяем на ошибку
;---
ZSEG SEGMENT
ZSEG ENDS

 
« Предыдущая статья   Следующая статья »