|
|
СтекПродолжение. Начало см. “В мир информатики” № 102–104 (“Информатика” № 2–4/2008)Пример 2. Использование стека для вызова подпрограммЗначение этого примера трудно переоценить, поскольку он иллюстрирует механизм работы подпрограмм: их вызов и последующий возврат в точку вызова. Все программное обеспечение базируется на использовании подпрограмм, а такие конструкции языков программирования, как процедура или функция, также реализуются на базе инструкций вызова подпрограмм и возврата из них. В обеспечении механизма возврата в точку вызова важнейшую роль играет область памяти, организованная в виде стека. Итак, опишем две примитивные подпрограммы, первая из которых задает значение регистра AX, а вторая — BX, и вызовем их. Последовательность действий по реализации задачи продемонстрирована в протоколе 2. Сначала вводится текст программы. Ее первая команда предназначена для обхода подпрограмм и перехода на начало основной программы. Поскольку точка входа пока еще неизвестна, переход набирается формально и позднее будет уточнен. Далее с адреса 102 вводится подпрограмма задания значения регистра BX. Она завершается инструкцией RET (от англ. return — возвращаться), которая обеспечит возврат в нужное место основной программы. Аналогично с адреса 106 набирается программа, определяющая значение регистра AX. Основная программа начинается с адреса 10a, на который необходимо будет не забыть перенастроить самую первую инструкцию нашей программы. Собственно основная программа состоит из вызова описанных выше подпрограмм с помощью инструкции CALL (переводится как “вызов”). Последняя инструкция выхода в операционную систему INT 20 поставлена для целей страховки — реально мы не будем ее исполнять. Командой –a100 исправим переход на начало программы и потом проверим правильность набора. Как и в первом примере, установим указатель стека SP на 170, чтобы легче было анализировать содержимое стека. Командами –r и –d проконтролируем текущее состояние регистров и стековой области памяти. Все готово к проведению эксперимента. Выполним первые три команды программы
— JMP 10A, CALL 102 и MOV BX,1111.
Учитывая уже накопленный опыт работы с
отладчиком Debug, подробно опишем только
интересующую нас сейчас команду вызова
подпрограммы; остальное проверьте по протоколу 2
самостоятельно. Как следует из теоретического
описания, вызов подпрограммы выполняет два
важных действия: запоминание в стеке адреса
возврата в вызывающую программу и переход на
начало вызываемой подпрограммы. Проконтролируем
по протоколу: указатель стека уменьшился на 2, т.е.
в стек занесен двухбайтовый адрес возврата.
Распечатав на экране область стека, обнаружим,
что в “верхушке” стека лежат байты 0d
01, что после необходимой перестановки (в
очередной раз напомним, что процессор Intel байты
сохраняет в обратном порядке!) дает адрес 10d. Взглянув на имеющуюся у нас
распечатку, убедимся, что это действительно
адрес следующей за командой CALL 102
инструкции. Итак, в стеке за- Итак, при вызове подпрограммы процессор аппаратным образом запоминает текущее положение счетчика команд, а затем заносит в IP адрес подпрограммы. В итоге следующей будет выполняться первая инструкция подпрограммы. Вернемся к протоколу 2. Мы остановились перед выполнением инструкции RET, которая призвана обеспечивать возврат из подпрограммы. Введем команду трассировки t и проанализируем, что произойдет. Указатель стека SP вернулся в исходное состояние, следовательно, хранившееся в стеке значение адреса возврата было считано. Куда именно — разумеется, в счетчик IP, который действительно указывает на хорошо известный нам адрес 10d. Фактически процессор выполнил действие POP IP, хотя в таком виде инструкцию никогда не пишут. Для нас важно подчеркнуть, что восстановление счетчика IP мало чем отличается от восстановления из стека регистра AX, которое разбиралось в предыдущем примере. Итак, благодаря сохраненной в стеке информации процессор вернулся строго в то место, откуда “ушел” в подпрограмму (точнее говоря, на следующую после вызова подпрограммы инструкцию). Работа подпрограммы 106 происходит аналогично, в чем предлагаем читателям убедиться самостоятельно. Задания для самостоятельной работы1. Измените обсуждаемую программу так, чтобы вторая подпрограмма (определяющая AX) вызывалась не из основной программы, а из подпрограммы 102 (так называемые “вложенные подпрограммы”). Пронаблюдайте, как в стеке в некоторый момент времени оказываются оба адреса возврата. 2. Напишите подпрограмму, суммирующую значения в регистрах AX и BX, и обратитесь к ней, предварительно задав исходные значения слагаемых. Не забывая о том, что сумма, как и слагаемые, представлена в шестнадцатеричном виде, убедитесь в правильности результата. 3. Проделайте то же самое упражнение для суммирования BX и CX, а сумму верните в DX. Новая проблема здесь заключается в том, что все арифметические операции производятся только в AX. Поэтому рекомендуемое решение может иметь следующий вид: PUSH AX Обязательно обратите внимание, что благодаря применению стека удается сохранить иллюзию неизменности AX, хотя этот регистр активно использовался в подпрограмме. Протокол 2
Е.. А.. Еремин,
| |