|
|
|
|
Назначение и использование локального ЗУ в памяти
Имеется три способа распределения памяти для переменных. Будем рассматривать локальное ЗУ (локальную память) в оперативной памяти и локальную
память в стеке. Сейчас рассмотрим локальное ЗУ в распределяемой памяти. Распределяемая память должна получаться из неиспользуемой памяти системы (часто
называемой пулом памяти). MS-DOS поддерживает функции, которые могут быть использованы для распределения, перераспределения и установки размера блоков
системной памяти. После распределения памяти программист может реализовать свою личную (персональную) схему управления памяти для управления памятью в
узком участке. Однако сейчас сконцентрируем свое внимание на возможностях MS-DOS, начиная с функции 48h - "распределить память".
После получения блока памяти, программа должна иметь возможность его адресации. Память, распределяемая MS-DOS, предоставляется в "кусках" по 16
байтов, называемых "paragraph" (параграфом). MS-DOS возвращает указатель на эту память, которая содержит 16-битовый адрес памяти блока. Сегменты
адресуются как параграфы, при этом указатель должен быть загружен в один из регистров сегмента (но не в регистр CS!). Обычно для повторного доступа к
блоку памяти используется либо сегмент данных, либо внешний сегмент. Если подпрограмма, которая распределяла память, не является главной подпрограммой
программы, то старое значение регистра сегмента должно сохраняться и восстанавливаться перед выходом из подпрограммы. Кроме того, перед выходом из
подпрограммы распределенная память должна быть возвращена в систему. Для возврата распределенной памяти блока в систему используется функция 49h MS-DOS
- "освободить распределенную память". В листинге 2-11 показано, как подпрограмма исполняемой программы .EXE будет распределять, использовать и освобождать
память, используемую как локальную память (локальное ЗУ).
Листинг 2-11. Распределение локальной памяти посредством MS-DOS
-----------------------------------------------------------------
common SEGMENT ; общие данные, используемые всеми
com_1 dw ?
com_2 db 14 DUP (?)
common ENDS
dummy_dat STRUC ; описание структуры,
dummy_1 dw ? ; используемой с
dummy_2 db 14 DUP (?) ; распределенной памятью
dummy_dat ENDS
ASSUME ds:common ; доступ к данным COMMON
local_example PROC NEAR ; процедура example
push ds ; сохранение предыдущего DS
B8 ---- R mov ax,common ; COMMON настраиваемая MS-DOS
mov ds,ax
push es ; сохранение предыдущего ES
mov ah,048h ; распределение памяти
mov bx,1 ; запрос 1 блока (16 байт)
int 21h ; вызов MS-DOS
jc not_alloc ; перенос означает сбой распред-я
mov es,ax ; если распределена, то ее адрес
;
; три примера адресации
;
A1 0000 R mov ax,com_1 ; надлежащий сег.-предполагается DS
B8 0000 mov ax,dummy_1 ; ошибочный сег.-непосредственный
26: A1 0000 mov ax,es:dummy_1 ; надлежащий сег.-замещаемый
;
mov ah,049h ; освобождение распредел. памяти
int 21h ; вызов MS-DOS
jnc free_ok ; нет переноса т.е. хорошо
not_alloc:
; Сообщение об ошибке, если сбой, распр-я или удал-я
free_ok
pop es ; восстановление ES
pop ds ; восстановление DS
ret
local_example ENDP ; конец примера
-----------------------------------------------------------------
|
Листинг 2-11 содержит оба вызова функций MS-DOS "Распределить память" и "Освободить память". Вместо регистра DS для указания на только что
распределенную память, был использован регистр ES, а регистр DS зарезервирован для доступа к области общих переменных программы. Заметим, что в
отличие от примера стека, для выполнения доступа к используемой структуре, определенной здесь, требуется оператор замещения сегмента (:). Без замещения
сегмента инструкция mov ax,dummy_1 не выполняет генерацию ссылки на память, используя регистр ES, но взамен этого генерирует загрузку смещения (в нашем
случае нуль) в регистр AX. При добавлении замещения сегмента к инструкции mov ax,es:dummy_1 макроассемблер MASM генерирует передачу памяти из смещения
dummy_1 во внешний сегмент. Замещение сегмента в листинге 2-11 показано с байтом префикса 26:.
При использовании в программе нескольких сегментов данных программист несет ответственность за управление используемыми областями данных. Например,
если программа X распределяет локальную память и обновляет регистр DS для доступа к этой области, то программист должен помнить о том, что эта
область данных по умолчанию принимается областью данных для всех программ, вызываемых программой X. Общая область данных, которая была определена в
программе, доступна еще путем загрузки либо регистра DS, либо регистра ES из переменной сегмента, как показано в листинге 2-6. Программы, изменяющие
содержимое своих регистров сегмента, должны сохранять и восстанавливать первоначальные значения регистров сегмента для предотвращения своих
порождаемых задач от ошибок.
Всякий раз, когда в программе используется более одного сегмента данных или внешних сегментов, программист должен быть очень внимателен к директивам
ASSUME (присвоить), используемым в программе. При ассемблировании обычных ссылок на память макроассемблер MASM сначала ищет их таблицу внешних символов
для имен переменных, к которым осуществляется доступ. Если MASM найдет переменную в таблице символов, он пытается создать ссылку, используя
сегмент, в котором определена эта переменная. Если этот сегмент отсутствует (т.е. отсутствует соответствующее предложение ASSUME), то MASM генерирует
сообщение об ошибке "Can't reach with segment reg" (нельзя найти регистр сегмента).
Если MASM не может найти переменную в таблице символов, то он предполагает, что она находится в сегменте данных. Если и это предположение окажется
неудачным, то MASM пытается исправить ошибку во время второй передачи путем присоединения к инструкции префикса замещения сегмента. При неудаче,
получение этого байта вызывает другое сообщение об ошибке "Phase error between passes" (ошибка фазы между передачами).
В случае неудачи или ссылки "вперед", т.е. когда имя переменной еще не находится в таблице символов, программист должен использовать оператор
замещения сегмента (:) для более чистого определения макроассемблером MASM используемого сегмента. Для управления доступом в программе также
полезен оператор SEG. Этот оператор позволяет программисту получать значение сегмента (базовый адрес сегмента) для любой определенной переменной.
Ссылки, создаваемые с помощью предложения SEG, настраиваются MS-DOS и полезны для создания настраиваемых ссылок вместо абсолютных ссылок.
|
|