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


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

Стек

Продолжение. Начало см. “В мир информатики” № 102–104, 106 (“Информатика” № 2–4, 6/2008)

Пример 3. Использование стека для передачи параметров подпрограмме

Известно, что процедуры или функции языков программирования высокого уровня, как правило, имеют параметры. Поставим перед собой вопрос, как можно передавать параметры подпрограмме при ее вызове? В простейшем случае — через регистры, подобно тому, как вы это делали в ходе самостоятельных упражнений к предыдущему примеру. Но такой способ подходит не всегда (например, представьте себе, что параметром служит строка из 32 символов — тут уж никаких регистров не хватит!).

Существует другой, более универсальный метод передачи параметров подпрограммам. Его логика вполне естественна: поскольку для обеспечения возврата из подпрограмм, как мы уже убедились, активно используется стек, можно воспользоваться этой же самой областью памяти и для передачи параметров.

Рассмотрим пример, приведенный в протоколе 3. В нем простой демонстрационной подпрограмме 1 передаются два параметра, которые она помещает в регистры DX и CX.

Организуем передачу параметров в стеке согласно следующему рисунку.

Учитывая, что стек заполняется в сторону уменьшения адресов (вниз по рисунку), видим, что сначала в него должны заноситься параметры, а затем адрес возврата, автоматически образующийся при вызове подпрограммы.

Протокол 3

-a

1423:0100 jmp 100

1423:0102 mov bx,sp

1423:0104 mov cx,[bx+2]

1423:0107 mov dx,[bx+4]

1423:010A ret 4

1423:010D mov ax,1111

1423:0110 push ax

1423:0111 mov ax,2222

1423:0114 push ax

1423:0115 call 102

1423:0118 int 20

1423:011A

-a100

1423:0100 jmp 10d

1423:0102

-u

1423:0100 EB0B JMP 010D

1423:0102 89E3 MOV BX,SP

1423:0104 8B4F02 MOV CX,[BX+02]

1423:0107 8B5704 MOV DX,[BX+04]

1423:010A C20400 RET 0004

1423:010D B81111 MOV AX,1111

1423:0110 50 PUSH AX

1423:0111 B82222 MOV AX,2222

1423:0114 50 PUSH AX

1423:0115 E8EAFF CALL 0102

1423:0118 CD20 INT 20

1423:011A 0000 ADD [BX+SI],AL

...

-rsp

SP FFE8

:170

-t6

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=010D NV UP EI PL NZ NA PO NC

1423:010D B81111 MOV AX,1111

AX=1111 BX=0000 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 50 PUSH AX

AX=1111 BX=0000 CX=0000 DX=0000 SP=016E BP=0000 SI=0000 DI=0000

DS=1423 ES=1423 SS=1423 CS=1423 IP=0111 NV UP EI PL NZ NA PO NC

1423:0111 B82222 MOV AX,2222

AX=2222 BX=0000 CX=0000 DX=0000 SP=016E BP=0000 SI=0000 DI=0000

DS=1423 ES=1423 SS=1423 CS=1423 IP=0114 NV UP EI PL NZ NA PO NC

1423:0114 50 PUSH AX

AX=2222 BX=0000 CX=0000 DX=0000 SP=016C BP=0000 SI=0000 DI=0000

DS=1423 ES=1423 SS=1423 CS=1423 IP=0115 NV UP EI PL NZ NA PO NC

1423:0115 E8EAFF CALL 0102

AX=2222 BX=0000 CX=0000 DX=0000 SP=016A 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 89E3 MOV BX,SP

-d

1423:0100 EB 0B 89 E3 8B 4F 02 8B-57 04 C2 04 00 B8 11 11 .....O..W.......

1423:0110 50 B8 22 22 50 E8 EA FF-CD 20 00 00 34 00 12 14 P.""P.... ..4...

...

1423:0160 22 22 00 00 02 01 23 14-70 0E 18 01 22 22 11 11 ""....#.p...""..

1423:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

-t4

AX=2222 BX=016A CX=0000 DX=0000 SP=016A BP=0000 SI=0000 DI=0000

DS=1423 ES=1423 SS=1423 CS=1423 IP=0104 NV UP EI PL NZ NA PO NC

1423:0104 8B4F02 MOV CX,[BX+02] DS:016C=2222

AX=2222 BX=016A CX=2222 DX=0000 SP=016A BP=0000 SI=0000 DI=0000

DS=1423 ES=1423 SS=1423 CS=1423 IP=0107 NV UP EI PL NZ NA PO NC

1423:0107 8B5704 MOV DX,[BX+04] DS:016E=1111

AX=2222 BX=016A CX=2222 DX=1111 SP=016A 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 C20400 RET 0004

AX=2222 BX=016A CX=2222 DX=1111 SP=0170 BP=0000 SI=0000 DI=0000

DS=1423 ES=1423 SS=1423 CS=1423 IP=0118 NV UP EI PL NZ NA PO NC

1423:0118 CD20 INT 20

Проанализируем подпрограмму повнимательнее. Она начинается инструкцией MOV BX,SP, которая копирует содержимое указателя стека в регистр BX. Сравнив с рисунком, убедимся, что BX указывает на адрес возврата, BX + 2 — на второй параметр (для CX), а BX + 4 — на первый (для DX). Если учесть, что в квадратных скобках в ассемблере принято указывать содержимое памяти, адрес которого определяется при помощи регистра, то становится понятным назначение двух следующих команд: они заносят значения параметров из стека в соответствующие регистры. Подчеркнем, что поскольку доступ к стековой области мы ведем не “стековыми”, а “обычными” методами, указатель SP при этом не смещается. Данный прием лишний раз подтверждает тот факт, что стековая память не есть что-то самостоятельное, напротив, она часть обычного ОЗУ.

Подпрограмма завершается инструкцией RET 4, которая помимо возврата дополнительно обеспечивает очистку четырех байт памяти (два параметра). Технически данная константа 4 просто прибавляется к SP: 16C + 4 = 170, т.е. указатель стека возвращается в начальное положение SP0.

Разобравшись с основными идеями приема параметров, обратимся к последовательности действий, описываемых в протоколе 3.

Сначала обычным образом вводится программа, корректируется переход на начало и проверяется правильность набора. Значение SP традиционно устанавливается на 170. Далее по директиве t6 выполняется шесть первых команд программы. В результате в стек командой PUSH заносятся два параметра, и вызывается подпрограмма 102. Анализ содержимого стека показывает, что оно соответствует приведенному выше рисунку, т.е. пока все работает правильно.

Директива t4 выполняет следующие четыре команды, которые образуют подпрограмму. Здесь тоже все проходит “в штатном режиме”: параметры помещаются в регистры и при возврате из подпрограммы SP восстанавливается. Таким образом, эксперимент полностью подтверждает теоретические принципы передачи параметров подпрограмме, описанные выше.

Задания для самостоятельной работы

1. Рассмотренный пример описывает случай, когда в подпрограмму передается значение переменной (в языках программирования высокого уровня даже есть специальный термин — передача параметра по значению). Более общий случай (передача параметра по ссылке) состоит в том, что передается не значение переменной, а ее адрес в памяти. Нетрудно понять, что только последний механизм способен обеспечить возврат из подпрограммы выходных параметров. Возможный алгоритм занесения ответа в “выходную” переменную может выглядеть, например, так:

MOV BX,SP

MOV SI,[BX+4]

MOV [SI],AX

Здесь для временного хранения адреса переменной использован дополнительный регистр процессора SI.

Продумайте, как можно с помощью стека организовать выдачу результата подпрограммы. Проверьте свои идеи на практике.

2. Попробуйте написать и реализовать в отладчике Debug машинный аналог следующей несложной процедуры на языке Паскаль:

Procedure Test(x: integer;

Var y: integer);

Begin

y := x + 1

End;

Занесите в ячейку, выбранную под переменную x, некоторое начальное значение, организуйте вызов процедуры и добейтесь, чтобы в y появился требуемый ответ.

Окончание —
в следующем выпуске


1 Напомним, что все программы выполняются с помощью отладчика Debug. — Прим. ред.

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

TopList