Страница 15 из 20 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 |