MS-DOS и TASM 2.0. Часть 13. Стек.

Стек в ассемблере

Стек в ассемблере.

Работа процедур тесно связана со стеком. Стеком называется область программы для временного хранения данных. Стек в ассемблере работает по правилу «Первым зашёл — последним вышел, последним зашёл — первым вышел». В любой период времени в стеке доступен только первый элемент, то есть элемент, загруженный в стек последним. Выгрузка из стека верхнего элемента делает доступным следующий элемент. Это напоминает ящик, в который поочерёдно ложатся книги. Чтобы получить доступ к книге, которую положили первой, необходимо достать поочерёдно все книги, лежащие сверху. Элементы стека располагаются в специально выделенной под стек области памяти, начиная со дна стека по последовательно уменьшающимся адресам. Адрес верхнего доступного элемента хранится в регистре-указателе стека SP. Стек может входить в какой-либо сегмент или быть отдельным сегментом. Сегментный адрес стека помещается в сегментный регистр SS. Пара регистров SS:SP образует адрес доступной ячейки стека.

Работа со стеком: команды PUSH и POP.

Работа со стеком осуществляется с помощью команд PUSH и POP.

PUSH (PUSH)

Затолкать операнд в стек:

push источник.

POP (POP)

Извлечь операнд из стека:

pop приемник.

Примеры для PUSH и POP:

«Заталкиваем» регистры в стэк:

стек в ассемблере
push cs
стек в ассемблере
push bx
стек в ассемблере
push ax

«Выталкиваем» регистры из стэка в обратной очерёдности:

стэк в ассемблере
pop ax
стек в ассемблере
pop bx
стек в ассемблере
pop cx — ошибка: менять значение cx нельзя, поэтому значение cx помещаем в ax

Стек при работе с процедурой (функцией).

Теперь рассмотрим, как ведёт себя стек в ассемблере при работе с процедурой при использовании call и ret. Прогоним нашего «гоблина» через отладчик. Напомним, что IP — Указатель команд (Index Pointer). На каждом шаге выполнения программы указывает на адрес команды, следующей за исполняемой. Используем горячую клавишу F7 — Trace — пошаговое выполнение программы с заходом в циклы и процедуры.

cs:0113> call 0140;IP = 0113.

Вызываем процедуру коммандой call 0140. В IP будет занесено значение указателя на нашу процедуру и она начнёт выполняться.
cs:0116> jmp 0100;

Вызов функции командой call
Вызываем процедуру командой call

После выхода из процедуры программа продолжит выполнение с IP = 0116. Стек (sp=FFFE): ss:FFFE>0000.

Вызов функции командой call
Возвращаемся из процедуры к адресу, следующему за call в адрес cs:0116

Что интересно, команду ret tasm транслировал в jmp. В данном случае ассемблер самостоятельно использовал оптимизацию кода в сторону ускорения быстродействия. В некоторых случаях такое «самоуправство» не приветствуется (написание «вирусного» кода и др.). Такими «болезнями» болеют все ассемблеры — кто-то больше, кто-то меньше. Знания прийдут вместе с опытом. Можно посоветовать всегда не лениться и пропускать исполняемый файл через отладчик.

Сохранение регистров с помощью стека.

В программе на ассемблере активно используются регистры (ax, cx, bx и т.д.). При входе в процедуру, значения регистров необходимо сохранять. Это очень удобно делать при помощи стека («запушить регистры», используя PUSH). После этого регистры могут активно использоваться в коде функции. Значения их будет меняться, но мы можем легко его восстановить. При выходе из функции значения регистров восстанавливают при помощи команд POP (не забываем о правиле «последний зашёл — первый вышел»).

Как говорилось в начале статьи, одна из самых важных задач, эффективно решаемых при помощи стека — передача параметров функции. Это нам точно понадобиться при рассмотрения программирования в системах Windows, в особенности с использованием Win32 API функций. С учётом важности этого вопроса мы рассматриваем его в следующей статье: «MS-DOS и TASM 2.0. Часть 17. Конвенции вызова функции в ассемблере».

Рекомендуем обновить информацию, которая нам известна о данных («MS-DOS и TASM 2.0. Часть 10. Данные«).

Добавить комментарий