Стековые команды

Организация стека. Под стек можно отвести любую область оперативной памяти. Размер сегмента памяти не должен превышать 64 Кб и его начальный адрес должен быть выровнен по параграфу (кратен 16). На начало сегмента стека должен указывать сегментный регистр SS. Стек заполняется снизу вверх в сторону меньших адресов (рис. 21).

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

Для определения текущего положения вершины стека используется регистр SP. По его адресу содержится элемент, записанный в стек последним (точнее, смещение ячейки, отсчитанное от начала сегмента стека). Абсолютный адрес вершины стека задается регистровой парой SS:SP.

 

 
 

 


Рис. 21. Организация работы стека.

 

 

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

Чтобы зарезервировать место в памяти под стек, следует описать программный сегмент. В примере под сегмент стека выделяется k байтов:

 

S SEGMENT STACK

DB k DUP(?)

S ENDS

 

Имена ячейкам памяти, отведенным под стек, не присваиваются, т.к. доступ осуществляется не по именам, а косвенно, через указатель SP. Чаще всего не задают и начальные значения для ячеек стека. Перед началом работы со стеком следует настроить регистровую пару SS:SP: регистр SS должен указывать на начало сегмента стека, а SP – на его вершину. В начале работы стек, как правило, пуст, и значение k соответствует пустому стеку.

Стековые команды предназначены для записи слова в стек и считывания слова из стека. Инструкция PUSHзаносит в стек указанное слово данных (табл. 59). Флаги команда не изменяет.

 

Табл. 59. Команда PUSH.

Код Инструкция Описание
FF /6 PUSH r/m16 Занесение данных в стек из r/m16.
58+rw PUSH r16 Занесение данных в стек из r16.
PUSH ES Занесение в стек ES.
0E PUSH CS Занесение в стек CS.
PUSH SS Занесение в стек SS.
1E PUSH DS Занесение в стек DS.

 

 

По команде значение регистра SP уменьшается на 2 (вычитание по модулю 216), т.е. SP сдвигается вверх и указывает на свободную ячейку области стека, а затем в нее записывается операнд:

 

SP = (SP-2) mod 216, op Þ [SS:SP]

 

По команде PUSH SP процессоры 8086 и 80286 в стек записывают значение SP после изменения этого регистра, а последующие модели – значение SP до изменения. В микропроцессоре 8086 в команде PUSH в качестве операнда можно указывать любой сегментный регистр (например, PUSH CS), но нельзя указывать непосредственный операнд (например, PUSH 5). Поэтому если в стек требуется записать число, то это надо делать через регистр, например:

 

MOV АХ, 2

PUSH АХ

 

Впоследствии ограничение по записи непосредственного операнда было снято.

Инструкция POP извлекает из стека слово данных и заносит его в указанный операнд (табл. 60). Флаги команда не изменяет. По команде слово из ячейки, на которую указывает пара SS:SP, пересылается в операнд, а затем SP увеличивается на 2 (сложение по модулю 216), т.е. сдвигается вниз:

 

[SS:SP] Þ op, SP = (SP+2) mod 216

 

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

 

 

Табл. 60. Команда POP.

Код Инструкция Описание
8F /0 POP m16 Занесение данных из стека в m16.
58+rw POP r16 Занесение данных из стека в r16.
1F POP DS Занесение данных из стека в DS.
POP ES Занесение данных из стека в ES.
POP SS Занесение данных из стека в SS.

 

 

Существует еще несколько команд работы со стеком. Инструкция PUSHF заносит в вершину стека содержимое регистра FLAGS, а инструкция POPF его извлекает (табл. 61). Флаги команды не изменяют.

 

 

Табл. 61. Команда PUSHF и POPF.

Код Инструкция Описание
9C PUSHF Запись в стек содержимого регистра флагов.
9D POPF Загрузка слова из вершины стека в регистра флагов.

 

 

Команды PUSHA и РОРА появились в микропроцессоре 80186. Команда PUSHA сохраняет в стеке значения регистров общего назначения в последовательности: АХ, СХ, DX, ВХ, SP, BP, SI и DI. Команда РОРА считывает из стека 8 слов и присваивает их регистрам в обратной последовательности. Команды полезны при работе с процедурами, сохраняя значения регистров при входе в процедуру и восстанавливая их прежние значения при выходе из нее.

Типичные приемы работы со стеком. В связи с ограниченным количеством регистров общего назначения стек используется для временного хранения содержимого регистров. Когда регистр, например AX, необходим для работы, его текущее значение может быть помещено в стек. После работы с регистром, прежнее значение должно быть восстановлено:

 

PUSH AX

... ; использование AX

POP AX

 

Наряду с сохранением данных стек может быть применен в роли буфера при пересылках чисел из одной ячейки памяти в другую, когда не следует изменять содержимое регистров. Например, присваивание переменных размером в слово X:=Y можно определить так:

 

PUSH Y

POP X

 

Такая схема удобна при переносе содержимого одного сегментного регистра в другой. Обычно загрузка одного сегментного регистра из другого требует сначала загрузки его значения в промежуточный регистр:

 

MOV AX, CS

MOV DS, AX

 

Каждая из команд имеет длину в несколько байт, и разрушает содержимое AX. Альтернативой может быть последовательность:

 

PUSH CS

POP DS

 

Команды в памяти занимают два байта, однако, они исполняются дольше, т.к. требуются дополнительные циклы чтения-записи в стек.

Стековые команды не осуществляют проверку на выход за пределы стека. Например, если стек пуст и применяется команда чтения из стека, то ошибка не будет зафиксирована (будет считано слово, следующее за сегментом стека). Стек полон, когда вершина стека достигла начала области, выделенной для стека, т.е. смещение вершины стека равно 0. При пустом стеке в регистре SP находится число, равное размеру области стека в байтах:

 

SP = 0 - стек полон

SP = k - стек пуст

 

Для очистки стека существует несколько вариантов действий. Можно выполнить команду POP заданное число. Оптимальным решением будет увеличение значения регистра SP на 2*N:

 

ADD SP, 2*N ; очистка стека от N слов

 

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

 

MOV AX, SP

... ; записи в стек

MOV SP, AX

 

Команды PUSH и POP предоставляют доступ к вершине стека, но иногда необходим доступ к более «глубоким» элементам стека (рис. 22). Пусть в стеке записано три слова и требуется переслать в регистр АХ копию третьего сверху элемента стека (AX = w3). Смещение третьего слова стека равно 4, поэтому следует установить регистр ВР на вершину стека и использовать выражение [ВР+4]:

 

MOV BP, SP

MOV АХ, [ВР+4]

 

Поскольку доступ к внутренним элементам стека приходиться делать довольно часто, регистр ВР выделен особо и по умолчанию сегментируется по SS. Использовать регистр SP, т.е. выражение [SP+n], нельзя, т.к. SP не относится к числу регистров-модификаторов.

Напомним, что если в команде модифицируется адрес и среди модификаторов есть регистр ВР, по умолчанию выбирается регистр SS. Поэтому указанная команда транслируется как MOV AX, SS:[BP+n], и, следовательно, считывание будет происходить из сегмента стека. Если вместо ВР используется другой регистр-модификатор, например ВХ, тогда префикс должен быть указан явно: MOV AX, SS:[BX+n].

 

 

 

 


Рис. 22. Содержимое стека.