Магда Юрий
Шрифт:
Исходный текст фрагмента кода, используемого вместо команды loopпе, выделен жирным шрифтом. Он очень напоминает программный код из предыдущего примера, с той лишь разницей, что команда jne по смыслу программы заменена командой je, кроме того, изменилась величина смещения (8 вместо 7). Смещение зависит от объема памяти, занимаемого пропускаемыми командами, а в этом фрагменте вместо dec CX используется для разнообразия команда dec CL, занимающая объем памяти на 1 байт больше.
Листинг 5.16. Замена команды loopne в программе из листинга 5.4
Помимо рассмотренных простейших вариантов можно разработать и другие способы модификации программного кода с командами loop СС. Автор надеется, что материал этой главы окажет помощь в создании новых, более эффективных алгоритмов обработки данных и модификации уже существующих.
Глава 6Процедуры на языке ассемблера
В большинстве программ встречаются фрагменты программного кода, которые нужно неоднократно выполнять и, следовательно, повторять одну и ту же последовательность команд. Такие фрагменты программного кода целесообразно выделить из программы, оформив в виде подпрограмм или процедур, и обращаться к ним всякий раз, когда основной программе потребуется их выполнение.
Немного о терминологии. В дальнейшем термины «подпрограмма» и «процедура» будут использоваться как синонимы (процедура является одной из форм реализации подпрограммы). Везде в этой главе и далее будем считать термины «подпрограмма» и «процедура» тождественными и полагать, что оба они представляют группу команд, заключенных между директивами ргос и endp. По отношению к подпрограмме, или процедуре, остальную часть программы принято называть основной или вызывающей программой. Эта глава посвящена принципам разработки подпрограмм (процедур) на языке ассемблера.
Подпрограммы могут находиться как в исполняемом файле основной программы, так и в отдельном объектном файле, который включается в файл основной программы при помощи компоновщика. Это означает, что исходный текст подпрограммы может помещаться в файл с расширением ASM и компилироваться автономно в файл объектного модуля, имеющий расширение OBJ.
Существует еще один способ использования автономных подпрограмм, который нередко применяется в 32-разрядных приложениях Windows: можно создать библиотеку динамической компоновки (Dynamic Link Library, DLL), поместив в нее программный код процедуры. В этом случае основная программа сможет определенным образом получить доступ к процедуре, находящейся в DLL. Создание и функционирование DLL тесно связано с архитектурой операционных систем Windows, что само по себе является отдельной темой, поэтому ограничимся рассмотрением классического варианта применения подпрограмм с использованием объектных файлов.
Для функционирования подпрограмм большое значение имеет правильное использование механизма стековых операций, поэтому прежде всего проанализируем принципы выполнения таких операций.
6.1. Организация стека
Стек представляет собой специальную область памяти, которая служит для временного хранения данных и адресов. Для адресации стека используются регистры 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 immed
Здесь reg16/reg32 – один из 16– или 32-разрядных регистров, тет16/тет32 – переменная в памяти (16 или 32 разряда), segreg – один из сегментных регистров (CS, DS, ES), a immed – непосредственное значение. Команда push с непосредственным операндом (immed) в процессорах Intel Pentium недопустима.
Существуют специальные модификации команды push. Так, например, для сохранения 16-разрядного регистра флагов процессора в стеке используется команда pushf, a для сохранения 32-разрядного регистра флагов – команда pushfd. Последняя команда присутствует только в процессорах, начиная с 80386. Наконец, существуют специальные форматы команды push, позволяющие сохранить в стеке все регистры процессора:
– pusha – помещает в стек все 16-разрядные регистры (АХ, ВХ, СХ, DX, SP, BP, s1 , DI);
– pushad – помещает в стек все 32-разрядные регистры (ЕАХ, ЕВХ, ЕСХ, EDX, ESP, EBP, ESI, EDI).
Приведу несколько примеров использования команды push и ее модификаций.
Предположим, что в стеке находится единственное значение, равное 7EE3h (рис. 6.1).
Рис. 6.1. Начальное состояние стека
Выполним команды
mov BX, 2CE9h
push BX