Главная страница «Первого сентября»Главная страница журнала «Информатика»Содержание №6/2008


В мир информатики
Эксперименты

Стек

Продолжение. Начало см. “В мир информатики” № 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 инструкции. Итак, в стеке за-
помнен адрес возврата (по сути дела, процессор выполнил команду PUSH IP, где IP — это счетчик адреса текущей команды программы; сравните с PUSH AX). Теперь, когда IP надежно сохранен, остается занести в него адрес начала подпрограммы 102, что и было сделано, как свидетельствует протокол 2 на с. 44.

Итак, при вызове подпрограммы процессор аппаратным образом запоминает текущее положение счетчика команд, а затем заносит в 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
MOV AX,CX
ADD AX,BX
MOV DX,AX
POP AX

Обязательно обратите внимание, что благодаря применению стека удается сохранить иллюзию неизменности AX, хотя этот регистр активно использовался в подпрограмме.

Протокол 2
  

-a

1423:0100 jmp 100
1423:0102 mov bx,1111
1423:0105 ret
1423:0106 mov ax,2222
1423:0109 ret
1423:010A call 102
1423:010D call 106
1423:0110 int 20
1423:0112
-a100
1423:0100 jmp 10a
1423:0102
-u
1423:0100 EB08         JMP      010A
1423:0102 BB1111     MOV     BX,1111
1423:0105 C3 RET
1423:0106 B82222     MOV     AX,2222
1423:0109 C3 RET
1423:010A E8F5FF     CALL   0102
1423:010D E8F6FF     CALL   0106
1423:0110 CD20         INT      20
1423:0112 0000         ADD      [BX+SI],AL
...
-rsp
SP FFEE
:170
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=0170 BP=0000 SI=0000 DI=0000
DS=1423 ES=1423 SS=1423 CS=1423 IP=0100 NV UP EI PL NZ NA PO NC
1423:0100 EB08                JMP 010A
-d
1423:0100 EB 08 BB 11 11 C3 B8 22-22 C3 E8 F5 FF E8 F6 FF ......."".......
1423:0110 CD 20 00 00 00 00 00 00-00 00 00 00 34 00 12 14 . ..........4...
...
1423:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1423:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-t3
AX=0000 BX=0000 CX=0000 DX=0000 SP=0170 BP=0000 SI=0000 DI=0000
DS=1423 ES=1423 SS=1423 CS=1423 IP=010A NV UP EI PL NZ NA PO NC
1423:010A E8F5FF CALL 0102
AX=0000 BX=0000 CX=0000 DX=0000 SP=016E BP=0000 SI=0000 DI=0000
DS=1423 ES=1423 SS=1423 CS=1423 IP=0102 NV UP EI PL NZ NA PO NC
1423:0102 BB1111 MOV BX,1111
AX=0000 BX=1111 CX=0000 DX=0000 SP=016E BP=0000 SI=0000 DI=0000
DS=1423 ES=1423 SS=1423 CS=1423 IP=0105 NV UP EI PL NZ NA PO NC
1423:0105 C3 RET
-d100
1423:0100 EB 08 BB 11 11 C3 B8 22-22 C3 E8 F5 FF E8 F6 FF ......."".......
1423:0110 CD 20 00 00 00 00 00 00-00 00 00 00 34 00 12 14 . ..........4...
...
1423:0160 00 00 00 00 00 00 00 00-05 01 23 14 70 0E 0D 01 ..........#.p...
1423:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-t
AX=0000 BX=1111 CX=0000 DX=0000 SP=0170 BP=0000 SI=0000 DI=0000
DS=1423 ES=1423 SS=1423 CS=1423 IP=010D NV UP EI PL NZ NA PO NC
1423:010D E8F6FF CALL 0106
-d100
1423:0100 EB 08 BB 11 11 C3 B8 22-22 C3 E8 F5 FF E8 F6 FF ......."".......
1423:0110 CD 20 00 00 00 00 00 00-00 00 00 00 34 00 12 14 . ..........4...
...
1423:0160 00 00 00 00 00 00 00 00-00 00 0D 01 23 14 70 0E ............#.p.
1423:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-t2
AX=0000 BX=1111 CX=0000 DX=0000 SP=016E BP=0000 SI=0000 DI=0000
DS=1423 ES=1423 SS=1423 CS=1423 IP=0106 NV UP EI PL NZ NA PO NC
1423:0106 B82222 MOV AX,2222
AX=2222 BX=1111 CX=0000 DX=0000 SP=016E BP=0000 SI=0000 DI=0000
DS=1423 ES=1423 SS=1423 CS=1423 IP=0109 NV UP EI PL NZ NA PO NC
1423:0109 C3 RET
-d100
1423:0100 EB 08 BB 11 11 C3 B8 22-22 C3 E8 F5 FF E8 F6 FF
......."".......
1423:0110 CD 20 00 00 00 00 00 00-00 00 00 00 34 00 12 14 . ..........4...
...
1423:0160 00 00 00 00 22 22 00 00-09 01 23 14 70 0E 10 01 ....""....#.p...
1423:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-t
AX=2222 BX=1111 CX=0000 DX=0000 SP=0170 BP=0000 SI=0000 DI=0000
DS=1423 ES=1423 SS=1423 CS=1423 IP=0110 NV UP EI PL NZ NA PO NC
1423:0110 CD20 INT 20
-d100
1423:0100 EB 08 BB 11 11 C3 B8 22-22 C3 E8 F5 FF E8 F6 FF ......."".......
1423:0110 CD 20 00 00 00 00 00 00-00 00 00 00 34 00 12 14 . ..........4...
...
1423:0160 00 00 00 00 22 22 22 22-00 00 10 01 23 14 70 0E ....""""....#.p.
1423:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

Е.. А.. Еремин,
г. Пермь

TopList