Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1




НазваниеКонспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1
страница9/41
Дата публикации03.03.2013
Размер5.22 Mb.
ТипКонспект
uchebilka.ru > Информатика > Конспект
1   ...   5   6   7   8   9   10   11   12   ...   41

Лекция 6

^ Органзация стека.
Организация стека
Стек представляет собой специальную область памяти, которая служит для вре­менного хранения данных и адресов. Для адресации стека используются регистры SS:SP (16-разрядные приложения) и SS: ESP (32-разрядные программы). Регистр SP (ESP) называется указателем стека и содержит 16- или 32-разрядный адрес по­следнего элемента, помещенного в стек. Последнее значение, помещенное в стек, извлекается первым. Подобная структура называется LIFO (Last In, First Out — прибыл последним, обслужен первым). Стек растет к меньшим адресам, то есть последнее значение, поступившее в стек, хранится по наименьшему адресу.

Несмотря на то что память в процессорах х86 имеет байтовую организацию, минимальный размер операнда, которым оперируют команды стековых опера­ций, равен слову (2 байта). По этой причине данные в стеке отстоят друг от друга на величину, кратную двум. Например, при помещении в стек слова значе­ние указателя стека SP (ESP) уменьшается на 2, при помещении двойного слова — на 4 и т. д. При этом младшие байты, операндов помещаются в стек по младшим адресам, а старшие байты — по старшим адресам.

Для того чтобы поместить какое-либо значение в стек, нужно использовать команду push. Эта команда в качестве параметра может принимать любой 16- или 32-разрядный регистр либо ячейку памяти. При этом содержимое указателя сте­ка SP (ESP) уменьшается на 2 (для слова) или на 4 (для двойного слова). Команда допускает один из форматов:
push regl6/reg32

push meml6/mem32

push segreg

push limed
Здесь reg16/reg32 — один из 16- или 32-разрядных регистров, тет16/тет32 — переменная в памяти (16 или 32 разряда), segreg — один из сегментных регистров (CS, DS, ES), a immed — непосредственное значение. Команда push с непосредствен­ным операндом (immed) в процессорах Intel Pentium недопустима.

Существуют специальные модификации команды push. Так, например, для со­хранения 16-разрядного регистра флагов процессора в стеке используется коман­да pushf, а для сохранения 32-разрядного регистра флагов — команда pushfd. По­следняя команда присутствует только в процессорах, начиная с 80386. Наконец, существуют специальные форматы команды push, позволяющие сохранить в сте­ке все регистры процессора:


  • pusha — помещает в стек все 16-разрядные регистры (АХ, ВХ, СХ, DX, SP, BP, SI, DI);

  • pushad — помещает в стек все 32-разрядные регистры (ЕАХ, ЕВХ, ЕСХ, EDX, ESP, EBP, ESI, EDI).


Приведу несколько примеров использования команды push и ее модификаций.

Предположим, что в стеке находится единственное значение, равное 7EE3h (рис. 1).



Рис. 1. Начальное состояние стека

Выполним команды:
mov BX. 2CE9h

push BX
Команда push в этом фрагменте программного кода копирует содержимое ре­гистра ВХ в стек, при этом содержимое регистра SP уменьшается на 2 и стек начи­нает выглядеть так, как показано на рис. 2.



Рис. 2. Состояние стека после выполнения команды push BX




Рис. 3. Размещение двойного слова в стеке


Напомню, что минимальная размерность данных, которыми оперирует стек, равна 16 бит, поэтому содержимое регистра SP (ESP) не может увеличиться или уменьшиться на 1. Это означает, что нельзя поместить в стек или извлечь из стека данные размером в 1 байт. Указатель стека увеличивается (уменьшается) на 2 или 4 (для слова или двойного слова соответственно). Например, после выпол­нения следующего фрагмента кода содержимое стека будет таким, как показано на рис. 3:

После этой операции указатель стека уменьшается на 4, поскольку в него по­мещается двойное слово.

Извлечение данных из стека выполняется с помощью команды pop. При этом из стека извлекается слово (двойное слово) и помещается в указанный операнд. Эта команда в качестве параметра может принимать любой 16- или 32-разрядный регистр или ячейку памяти. При этом содержимое указателя стека SP (ESP) увели­чивается на 2 (для слова) или на 4 (для двойного слова).

Команда pop является зеркальной по отношению к push и использует те же ти­пы операндов, что и команда push. Кроме того, для извлечения содержимого реги­стра флагов из стека имеются команды popf (для 16-разрядного регистра флагов) и popfd (для 32-разрядного). Для того чтобы восстановить все регистры процессо­ра значениями из стека, в систему команд включены инструкции рора (для 16-раз­рядных регистров) и popad (для 32-разрядных). Например, следующая команда извлекает данные, помещенные в стек в предыдущем примере, и запоминает их в регистре EDX:]
pop EDX
После выполнения этой команды регистр ЕОХ будет содержать значение 4FE91A77h, а указатель стека уменьшится на 4 (рис. 4).



^ Рис 4. Содержимое стека после выполнения команды pop EDX

Как видно из предыдущих примеров, стек может обеспечивать временное хранение данных. Кроме того, с помощью команд push и pop можно организовать обмен данными между регистрами и памятью, причем операнды могут иметь раз­ный размер. В следующих примерах показана техника использования стека в раз­личных операциях:
mov EAX. 11223344h

push EAX

pop ВХ

pop СХ
Здесь команда push EAX помещает в стек двойное слово 11223344b. После выполнения команды pop BX из стека извлекается младшее слово, равное 3344h, и помещается в регистр ВХ. Указатель стека ESP при этом уменьшается на 2. Сле­дующая команда pop СХ извлекает из стека старшее слово, равное 1122h, и поме­щает его в регистр СХ. При этом содержимое регистра ESP опять уменьшается на 2.

...

.data

op DW 7777h

.code

...

push DS:op

pop AX

...

В этом примере в стек помещается значение 16-разрядной переменной ор (ко­манда push DS: op), при этом указывается сегмент данных, в котором определена переменная (регистр DS). Указатель стека SP после выполнения этой операции уменьшается на 2. Следующая команда pop AX извлекает содержимое стека в ре­гистр АХ и восстанавливает стек, увеличивая значение SP на 2. Таким образом, в ре­гистре АХ будет содержаться значение 7777h.

Следующий пример демонстрирует применение операций со стеком в 16-раз­рядном приложении. Исходный текст программы показан в листинге 1.

Листинг 1. Демонстрация стековых операций (16-разрядная версия)

.model small

.data

numl DW '91'

s1 DB "STRING 1 $"

^ S2 DB "STRING 2 $"

.code

start:

mov AX. @data

mov DS. AX

push DS:numl

lea SI. s2

push SI

lea DX. si

mov AH. 9h

int 21h

pop DX

int 21h

pop DX

xchg DH. DL

mov AH. 2h

int 21h

xchg DH. DL

int 21h

mov AX. 4c00h

int 21h

end start

end
Программа достаточно проста — она выводит на экран значения перемен­ной numl и символьных строк si и s2, причем вначале отображается содержи­мое строки si, затем — строки s2 и наконец — значение переменной numl. Сначала в стек помещается значение переменной numl (команда push DS:numl), затем — адрес строки s2:
push DS:numl

lea SI. s2

push s2

После этих операций указатель стека уменьшается на 4, а содержимое стека становится таким, как показано на рис. 5.



Рис. 5. Содержимое стека после помещения данных программы

Затем программа выводит на экран строку si:

lea DX.

si mov AH. 9h

int 21h

После этого из стека извлекается адрес строки s2 и помещается в регистр ОХ. Далее строка s2 выводится на экран:

pop DX

int 21h

К этому моменту в стеке остается значение переменной numl, а указатель стека SP уменьшается на 2. Следующая команда pop DX извлекает переменную numl из стека и помещает ее значение в регистр DX, при этом указатель стека еще раз уменьшается на 2. Последующие команды отображают содержимое DX на экране с учетом порядка размещения байтов в регистре:
pop DX

xchg DH. DL

mov AH. 2h

int 21h

xchg DH. DL

int 21h
Хочу сделать замечание: для временного хранения в стеке данных, представ­ленных строками или массивами, используются их адреса или, как их еще назы­вают, указатели. Адрес строки (или массива) одновременно является и адресом ее первого элемента. Например, адрес строки si из предыдущего примера совпа­дает с адресом символа S.

При выполнении операций со стеком вся ответственность за содержимое стека ложится на программиста, поэтому нужно быть очень внимательным. Если ка­кое-либо значение помещается в стек во время работы программы, то оно должно быть извлечено из стека перед ее завершением либо стек должен быть восстанов­лен каким-то другим способом. Несоблюдение этих требований приводит, как правило, к краху программы. Точно так же суммарный размер операндов, извле­ченных из стека, должен быть равным размеру помещенных в него данных.

Хорошо спроектированная программа перед завершением всегда восстанав­ливает указатель стека к тому значению, которое было перед началом ее выпол­нения.

Для операций с данными в стеке не обязательно использовать команды push и pop. Вспомним, что стек представляет собой всего лишь область оперативной памяти, поэтому для доступа к данным можно применять обычные команды ас­семблера, используя регистровую косвенную адресацию посредством регистра ВР (ЕВР). Для доступа к данным стека необходимо поместить содержимое указателя стека SP (ESP) в регистр ВР (ЕВР), после чего указать смещение данных. Следующий фрагмент программного кода демонстрирует такой подход (листинг 2).
.data

opl DW 1149h

op2 DW 0E37h .code

mov AX. @data

mov DS. AX

push DS:opl

push DS:op2

mov BP, SP

mov AX. word ptr [BP+2]

mov BX. word ptr [BP]
Здесь содержимое переменных opl и ор2 помещается в стек, причем значение opl оказывается по адресу [SP+2], а значение ор2 — по адресу [SP] (рис. 6).



Рис. 6. Содержимое стека после размещения переменных opl и ор2
Поскольку после выполнения команды mov ВР. SP регистр ВР содержит значе­ние SP, то значение переменной opl хранится по адресу [ВР+2], а значение ор2 — по адресу [ВР]. После выполнения последних двух команд данного фрагмента кода регистр АХ будет содержать 1149h, а регистр BX — 0E37h.

При разработке 32-разрядных приложений для процессоров Intel Pentium использовать регистр ЕВР для доступа к данным в стеке не обязательно — можно напрямую работать с указателем стека ESP. Например, с помощью следующего фрагмента программного кода вычисляется разность операндов ор2 и opl, которая затем помещается в регистр ЕАХ:
.686

.model flat

option casemap: none

.data

opl DD 145

op2 DD 98 .code

push opl

push op2

mov EAX. dword ptr [ESP] : содержимое opl -> EAX

sub EAX. dword ptr [ESP+4] : op2 - opl -> EAX
В последних двух примерах мы не акцентировали внимание на восстановле­нии указателя стека, хотя в ряде случаев применение обычных команд pop может оказаться неудобным или невозможным. В таких случаях можно воспользоваться еще одним способом восстановления стека — задействовать команду add:
add ESP. n
Здесь п — количество байтов, на которое следует продвинуть указатель сте­ка SP (ESP). Следующий пример демонстрирует восстановление указателя стека после того, как в стек были помещены три двойных слова (12 байт):
.code

push EAX push EBX push ECX

add ESP. 12
Поскольку команды push помещают в стек 12 байт (три двойных слова), то для восстановления указателя стека следует продвинуть его на это же число в сторо­ну увеличения адресов, что и делается с помощью команды add.

Далее мы проанализируем, как используется стек при выполнении подпрограмм.
Лекция 6

Процедуры на языке
ассемблера



В большинстве программ встречаются фрагменты программного кода, которые нужно неоднократно выполнять и, следовательно, повторять одну и ту же после­довательность команд. Такие фрагменты программного кода целесообразно вы­делить из программы, оформив в виде подпрограмм или процедур, и обращаться к ним всякий раз, когда основной программе потребуется их выполнение.

Немного о терминологии. В дальнейшем термины «подпрограмма» и «проце­дура» будут использоваться как синонимы (процедура является одной из форм реализации подпрограммы). Везде в этой главе и далее будем считать термины «подпрограмма» и «процедура» тождественными и полагать, что оба они представ­ляют группу команд, заключенных между директивами ргос и endp. По отношению к подпрограмме, или процедуре, остальную часть программы принято называть основной или вызывающей программой. Эта глава посвящена принципам разра­ботки подпрограмм (процедур) на языке ассемблера.

Подпрограммы могут находиться как в исполняемом файле основной про­граммы, так и в отдельном объектном файле, который включается в файл основ­ной программы при помощи компоновщика. Это означает, что исходный текст подпрограммы может помещаться в файл с расширением ASM и компилироваться автономно в файл объектного модуля, имеющий расширение OBJ.

Существует еще один способ использования автономных подпрограмм, кото­рый нередко применяется в 32-разрядных приложениях Windows: можно создать библиотеку динамической компоновки (Dynamic Link Library, DLL), поместив в нее программный код процедуры. В этом случае основная программа сможет определенным образом получить доступ к процедуре, находящейся в DLL. Соз­дание и функционирование DLL тесно связано с архитектурой операционных систем Windows, что само по себе является отдельной темой, поэтому ограничим­ся рассмотрением классического варианта применения подпрограмм с использова­нием объектных файлов.

Для функционирования подпрограмм большое значение имеет правильное использование механизма стековых операций, поэтому, прежде всего, проанализи­руем принципы выполнения таких операций.


Подпрограмма, в зависимости от выполняемых ею функций, может требовать передачи из вызывающей программы определенных данных, которые принято называть аргументами или параметрами и возвращать в вызывающую програм­му результат вычислений. Некоторые подпрограммы могут вообще не принимать никаких параметров и не возвращать результат. Чаще всего подпрограмма (про­цедура) оформляется так, как показано в следующем фрагменте кода:
...

mov AX, 0

mov BX, 0

jmp start

addl proc : точка входа в процедуру addl

inc AX

ret : возврат в вызывающую подпрограмму addl endp

subl proc : точка входа в процедуру subl

dec BX

ret : возврат в вызывающую программу

subl endp

start:

call addl : вызов процедуры addl

call subl : вызов процедуры subl

jmp start
Как видно из приведенного фрагмента кода, в начале процедуры (перед пер­вой выполняемой командой) должна находиться директива proc, а после по­следней выполняемой команды — директива endp. Процедура обязательно долж­на заканчиваться командой ret. В одном ассемблерном файле с расширением ASM можно размещать несколько процедур.

Точкой входа в процедуру считается директива proc. В директиве proc после имени процедуры не ставится двоеточие, хотя имя считается меткой и указывает на первую команду процедуры. Имя процедуры можно указать в команде перехода, и тогда будет осуществлен переход на первую команду процедуры.

Директива proc может принимать один из двух параметров: near или far. Пара­метр near указывает на то, что процедура является ближней, a far указывает на то, что процедура дальняя. Если параметр отсутствует, то считается, что процедура имеет тип near (поэтому параметр near обычно и не указывается).

К ближней (near) процедуре можно обращаться только из того сегмента ко­манд, где она объявлена, а к дальней (far) процедуре — из любых сегментов ко­манд, включая тот, где она объявлена. Для 32-разрядных приложений все вызовы процедур считаются ближними.

Следует отметить, что в языке ассемблера имена и метки, описанные в про­цедуре, должны быть уникальными и не должны совпадать с другими именами в программе. В языке ассемблера имеется возможность создавать вложенные процедуры, то есть процедуры внутри процедур, но особых преимуществ это не дает и используется относительно редко.

Можно обойтись и без явного определения процедуры, пометив первую стро­ку программы некоторой меткой, как проиллюстрировано в следующем фрагмен­те программного кода:
mov AX. О

mov BX. О next:

call addl

call subl

jmp next addl: : подпрограмма, начинающаяся с метки

inc AX

ret subl: : подпрограмма, начинающаяся с метки

dec BX

ret

В этом случае отсутствуют директивы ргос и endp и говорят, что подпрограмма (процедура) определяется неявно. Подобные записи процедур используются редко, поскольку значительно затрудняют анализ исходных текстов и отладку программ. Мы не будем применять такое определение процедур, а воспользуемся директивами ргос и endp, как было сказано в начале главы. Вызов процедуры вы­полняется с помощью команды cal1, которая передает управление процедуре, со­хранив в стеке адрес возврата в вызывающую программу. Процедура должна за­вершаться командой ret, которая извлекает из стека адрес возврата и возвращает управление команде, следующей за командой call.

Рассмотрим более подробно механизмы работы команд cal 1 и ret. Особое зна­чение в механизме вызова процедуры и возврата из нее имеет стек. Поскольку в стеке хранится адрес возврата, то процедура, использующая его для хранения промежуточных результатов, должна к моменту выполнения команды ret восста­новить стек в том состоянии, в котором он находился перед ее вызовом. В этом случае говорят, что процедура должна восстановить, или очистить, стек.

В момент вызова процедуры команда call помещает в стек адрес команды, следующей непосредственно за call, уменьшая значение указателя стека SP (ESP). Команда ret вызываемой процедуры использует этот адрес для возврата в вы­зывающую программу, автоматически увеличивая при этом указатель верши­ны стека.

Типы адресации (near или far) команд ret и call должны соответствовать друг другу. Вызываемая процедура может вызвать с помощью команды call следую­щую процедуру и т. д., поэтому стек должен иметь достаточный размер для того, чтобы хранить в нем все записываемые данные.

Следует сказать, что команда ret не анализирует состояние или содержимое стека. Она извлекает из вершины стека слово или двойное слово, в зависимости от типа адресации, полагая, что это адрес возврата, по которому передается управление. Если к моменту выполнения команды ret указатель стека окажется смещенным в ту или иную сторону, содержимое вершины стека может пред­ставлять все что угодно, поэтому передача управления по этому адресу приведет к краху программы.

Команда call может иметь один из перечисленных ниже форматов вызова:


  • прямой ближний (в пределах текущего программного сегмента);

  • прямой дальний (вызов процедуры, расположенной в другом программном сегменте);

  • косвенный ближний (в пределах текущего программного сегмента с ис­пользованием переменной, содержащей адрес перехода);

  • косвенный дальний (вызов процедуры, расположенной в другом программ­ном сегменте, с использованием переменной, содержащей адрес пере­хода).


Тип адресации при вызове процедуры зависит от используемой модели па­мяти. Директива .model автоматически устанавливает атрибут near или far для вызываемых процедур, при этом модели tiny, small и compact устанавливают ат­рибут near, а модели medium, large и huge — атрибут far. Ассемблер генерирует far-вызовы для моделей medium, large и huge автоматически. Для 32-разрядных приложений, использующих модель f 1 at, все вызовы процедур считаются ближ­ними (near).

Проанализируем более подробно форматы вызовов команды cal 1. Если исполь­зуется прямой ближний вызов, то команда call помещает в стек относительный адрес точки возврата в текущем программном сегменте и модифицирует указа­тель адресов команд EIP так, чтобы в нем содержался относительный адрес точки перехода в том же программном сегменте.

Требуемая для вычисления этого адреса величина смещения от точки возвра­та до точки перехода содержится в коде самой команды, занимающем 3 байта (код операции E8h плюс смещение к точке перехода).

Команда call прямого дальнего вызова помещает в стек два слова: вначале сегментный адрес текущего программного сегмента, затем относительный адрес точки возврата в этом программном сегменте. После этого выполняется модифи­кация регистров EIP и CS: в EIP помещается относительный адрес точки перехода в том сегменте, куда осуществляется переход, а в CS — селектор адреса для этого сегмента.

Оба эти значения извлекаются из кода команды, занимающего 5 байт (код операции 9Ah, эффективный адрес вызываемой процедуры и селектор сегмента). Для указания прямого дальнего вызова используется директива far ptr, которая говорит компилятору и компоновщику, что вызов является дальним.

В листинге 3 показан фрагмент программного кода, демонстрирующий даль­ний вызов процедуры.
1   ...   5   6   7   8   9   10   11   12   ...   41

Похожие:

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций для студентов заочной формы обучения направления 080201 (Информатика)
Предлагаемый конспект лекций представляет собой пособие по предмету “Теория информации”, который читается в Сумском государственном...

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций 2007 Экология. Конспект лекций. Для студентов специальностей...
Экология. Конспект лекций. Для студентов специальностей 080201 «Информатика», 090220 «Оборудование химических производств и предприятий...

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций по дисциплине информационные и телекоммуникационные...

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций по дисциплине “
«Компьютерная инженерия», специальности 091501 «Компьютерные сети и системы», 091502 «Системное программирование»

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций по курсу Выбранные вопросы информатики (часть 2)...
Точно так же как художник может выбирать для рисования различные инструменты, программист, создающий аплет Java, может выбирать различные...

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций по дисциплине “Статистика в машиностроении ” для студентов специальности
Конспект лекций предназначен для самостоятельного изучения студентами теоретической части курса “ Статистика в машиностроении ” (для...

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций по дисциплине “ основы защиты информации” для направления...
Министерство образования и науки украины восточноукраинский государственный университеТ

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций по курсу Начертательная геометрия
Конспект лекций по курсу начертательная геометрия (для студентов заочной формы обучения всех специальностей академии). Сост. Лусь...

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций и методические указания к выполнению контрольной работы к изучению курса
Конспект лекций и методические указания к выполнению контрольной работы по курсу “Проектирование специальных станочных и контрольных...

Конспект лекций по курсу Системное программирвание для специальности Информатика Лекция 1 iconКонспект лекций по курсу «Организация производства»
Конспект лекций по курсу «Организация производства» (для студентов и слушателей заочной формы обучения фпоизо специальностей 050100...

Вы можете разместить ссылку на наш сайт:
Школьные материалы


При копировании материала укажите ссылку © 2013
контакты
uchebilka.ru
Главная страница


<