Что такое push в ассемблере
PUSH Занесение операнда в стек
Команда push уменьшает на 2 содержимое указателя стека SP и заносит на эту новую вершину двухбайтовый операнд-источник (проталкивает в стек новое данное). Проталкивать в стек можно только целые слова (не байты). Программа должна строго следить за тем, чтобы каждой команде проталкивания в стек push отвечала обратная команда выталкивания из стека pop. Если стек используется для временного хранения некоторых данных, то извлекать эти данные из стека следует в порядке, обратном их сохранению.
В качестве операнда-источника может использоваться любой 16-разрядный регистр (включая сегментный) или ячейка памяти. Не допускается занесение в стек непосредственного значения, хотя некоторые трансляторы преобразуют команду вида
push ES:mem ;Сохранение содержимого
push DS ;слова памяти mem из
push BP ;дополнительного сегмента
;а также регистров DS и ВР
… ;
pop PP ;Восстановление из стека
pop DS ;трех операндов
pop ES: mem ;в обратном порядке
push DS ;Пересылка DS через стек
pop ES ;Теперь ES=DS
Допустима засылка в стек 32-битовых операндов (регистров и ячеек памяти), а также занесение в стек 8-, 16- и 32-битовых непосредственных значений. Каждое 8-битовое значение занимает в стеке целое слово. Операнды любого допустимого размера могут заноситься з стек вперемежку’, если это не вступает в противоречие с операциями по извлечению этих данных из стека.
push AX ; Сохранение в стеке регистра АХ
push 32h ;Сохранение в стеке
;константы 32h (она займет в
;стеке 1 слово)
push EAX ;Сохранение в стеке регистра
;ЕАХ (два слова стека)
386+ PUSHA Сохранение в стеке всех регистров
Команда pusha сохраняет в стеке содержимое всех регистров в следующем порядке: АХ, СХ, DX, ВХ, значение указателя стека SP перед выполнением данной команды, дачее ВР, SI и DI. Команда не имеет параметров и не воздействует на флаги процессора.
386+ PUSHAD Сохранение в стеке всех регистров в 32-разрядном режиме
Команда pushad сохраняет в стеке содержимое всех регистров в следующем порядке: EAX, ECX, EDX, ЕВХ, значение указателя стека ESP перед выполнением данной команды, далее EBP, ESI и EDI. Команда не имеет параметров и не воздействует на флаги процессора.
386+ PUSHFD Занесение в стек содержимого расширенного регистра флагов
Команда pushfd уменьшает на 4 содержимое указателя стека ESP и заносит на эту новую вершину содержимое расширенного регистра флагов EFALGS. При этом сохраняются все флаги процессора. Команда pushfd не имеет параметров и не воздействует на флаги процессора.
pushfd ;Содержимое регистра флагов
;сохраняется в стеке
RCL Циклический сдвиг влево через бит переноса
Команда гсl осуществляет сдвиг влево всех битов операнда. Если команда записана в формате
сдвиг осуществляется на 1 бит. В младший бит операнда заносится значение флага CF; старший бит операнда загружается в CF. Если команда записана в формате
Рис. П4. Действие команды rcl.
В качестве операнда команды rcl можно указывать любой регистр (кроме сегментного) или ячейку памяти размером как в байт, так и в слово. Не допускается использовать в качестве операнда непосредственное значение. Команда воздействует на флаги OF и CF.
clc ;Сбросим CF
mov AX, 7
rcl АХ,1 ‘ ;AX=000Eh=14, CF=0
clc ;Сбросим CF
mov BX,0FFFFh
rcl BX,1 ‘ ;BX=FFFEh, CF=1
clc ;Сбросим CF
mov DH,3
mov CL,4 ;Счетчик сдвигов
rcl DH,CL ;DH=30h=48, CF=0
Допустим сдвиг 32-битовых операндов. Допустимо указание числа битов сдвига как с помощью регистра CL, так и непосредственным значением. Максимальная величина сдвига составляет 31 бит.
mov EAX,0С0000003h
clc ;Сбросим CF
rcl EAX,2 ;EAX=0000000Dh, CF=1
PUSH Занесение операнда в стек
Команда push уменьшает на 2 содержимое указателя стека SP и заносит на эту новую вершину двухбайтовый операнд-источник (проталкивает в стек новое данное). Проталкивать в стек можно только целые слова (не байты). Программа должна строго следить за тем, чтобы каждой команде проталкивания в стек push отвечала обратная команда выталкивания из стека pop. Если стек используется для временного хранения некоторых данных, то извлекать эти данные из стека следует в порядке, обратном их сохранению.
В качестве операнда-источника может использоваться любой 16-разрядный регистр (включая сегментный) или ячейка памяти. Не допускается занесение в стек непосредственного значения, хотя некоторые трансляторы преобразуют команду вида
push ES:mem ;Сохранение содержимого
push DS ;слова памяти mem из
push BP ;дополнительного сегмента
;а также регистров DS и ВР
… ;
pop PP ;Восстановление из стека
pop DS ;трех операндов
pop ES: mem ;в обратном порядке
push DS ;Пересылка DS через стек
pop ES ;Теперь ES=DS
Допустима засылка в стек 32-битовых операндов (регистров и ячеек памяти), а также занесение в стек 8-, 16- и 32-битовых непосредственных значений. Каждое 8-битовое значение занимает в стеке целое слово. Операнды любого допустимого размера могут заноситься з стек вперемежку’, если это не вступает в противоречие с операциями по извлечению этих данных из стека.
push AX ; Сохранение в стеке регистра АХ
push 32h ;Сохранение в стеке
;константы 32h (она займет в
;стеке 1 слово)
push EAX ;Сохранение в стеке регистра
;ЕАХ (два слова стека)
386+ PUSHA Сохранение в стеке всех регистров
Команда pusha сохраняет в стеке содержимое всех регистров в следующем порядке: АХ, СХ, DX, ВХ, значение указателя стека SP перед выполнением данной команды, дачее ВР, SI и DI. Команда не имеет параметров и не воздействует на флаги процессора.
386+ PUSHAD Сохранение в стеке всех регистров в 32-разрядном режиме
Команда pushad сохраняет в стеке содержимое всех регистров в следующем порядке: EAX, ECX, EDX, ЕВХ, значение указателя стека ESP перед выполнением данной команды, далее EBP, ESI и EDI. Команда не имеет параметров и не воздействует на флаги процессора.
386+ PUSHFD Занесение в стек содержимого расширенного регистра флагов
Команда pushfd уменьшает на 4 содержимое указателя стека ESP и заносит на эту новую вершину содержимое расширенного регистра флагов EFALGS. При этом сохраняются все флаги процессора. Команда pushfd не имеет параметров и не воздействует на флаги процессора.
pushfd ;Содержимое регистра флагов
;сохраняется в стеке
RCL Циклический сдвиг влево через бит переноса
Команда гсl осуществляет сдвиг влево всех битов операнда. Если команда записана в формате
сдвиг осуществляется на 1 бит. В младший бит операнда заносится значение флага CF; старший бит операнда загружается в CF. Если команда записана в формате
Рис. П4. Действие команды rcl.
В качестве операнда команды rcl можно указывать любой регистр (кроме сегментного) или ячейку памяти размером как в байт, так и в слово. Не допускается использовать в качестве операнда непосредственное значение. Команда воздействует на флаги OF и CF.
clc ;Сбросим CF
mov AX, 7
rcl АХ,1 ‘ ;AX=000Eh=14, CF=0
clc ;Сбросим CF
mov BX,0FFFFh
rcl BX,1 ‘ ;BX=FFFEh, CF=1
clc ;Сбросим CF
mov DH,3
mov CL,4 ;Счетчик сдвигов
rcl DH,CL ;DH=30h=48, CF=0
Допустим сдвиг 32-битовых операндов. Допустимо указание числа битов сдвига как с помощью регистра CL, так и непосредственным значением. Максимальная величина сдвига составляет 31 бит.
mov EAX,0С0000003h
clc ;Сбросим CF
rcl EAX,2 ;EAX=0000000Dh, CF=1
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
После выхода из процедуры программа продолжит выполнение с IP = 0116. Стек (sp=FFFE): ss:FFFE>0000.
Возвращаемся из процедуры к адресу, следующему за call в адрес cs:0116
Что интересно, команду ret tasm транслировал в jmp. В данном случае ассемблер самостоятельно использовал оптимизацию кода в сторону ускорения быстродействия. В некоторых случаях такое «самоуправство» не приветствуется (написание «вирусного» кода и др.). Такими «болезнями» болеют все ассемблеры — кто-то больше, кто-то меньше. Знания прийдут вместе с опытом. Можно посоветовать всегда не лениться и пропускать исполняемый файл через отладчик.
Сохранение регистров с помощью стека.
В программе на ассемблере активно используются регистры (ax, cx, bx и т.д.). При входе в процедуру, значения регистров необходимо сохранять. Это очень удобно делать при помощи стека («запушить регистры», используя PUSH). После этого регистры могут активно использоваться в коде функции. Значения их будет меняться, но мы можем легко его восстановить. При выходе из функции значения регистров восстанавливают при помощи команд POP (не забываем о правиле «последний зашёл — первый вышел»).
Ассемблер. Процедуры
Обновл. 16 Сен 2021 |
Процедуры или подпрограммы очень важны в Ассемблере, так как большинство программ на ассемблере имеют большой размер. Процедуры идентифицируются по имени, после которого, собственно, и следует тело процедуры. Конец процедуры указывается стейтментом возврата.
Процедуры в Ассемблере
Ниже приведен синтаксис определения процедуры:
имя_процедуры:
тело_процедуры
.
ret
Процедура вызывается из другой функции с помощью инструкции CALL, которая содержит имя вызываемой процедуры в качестве аргумента:
Вызываемая процедура возвращает управление вызывающей процедуре с помощью инструкции RET.
Результат выполнения программы:
Стек как структура данных
Стек — это структура данных в памяти в виде массива, в котором эти данные могут храниться и удаляться из места, называемого «вершиной» стека. Данные, которые должны быть сохранены, «помещаются» в стек, а данные, которые должны быть извлечены — «выталкиваются» из стека. Стек — это структура данных типа LIFO (англ. «Last In, First Out» = «Последним пришёл, первым ушёл»), детально об этом читайте здесь.
PUSH операнд
POP адрес/регистр
Сохранять значения регистров в стеке перед их использованием можно следующим образом:
FasmWorld Программирование на ассемблере FASM для начинающих и не только
Учебный курс. Часть 20. Стек
Автор: xrnd | Рубрика: Учебный курс | 31-05-2010 |
Распечатать запись
Стеком называется структура данных, организованная по принципу LIFO («Last In — First Out» или «последним пришёл — первым ушёл»). Стек является неотъемлемой частью архитектуры процессора и поддерживается на аппаратном уровне: в процессоре есть специальные регистры (SS, BP, SP) и команды для работы со стеком.
Обычно стек используется для сохранения адресов возврата и передачи аргументов при вызове процедур (о процедурах в следующей части), также в нём выделяется память для локальных переменных. Кроме того, в стеке можно временно сохранять значения регистров.
Схема организации стека в процессоре 8086 показана на рисунке:
Стек располагается в оперативной памяти в сегменте стека, и поэтому адресуется относительно сегментного регистра SS. Шириной стека называется размер элементов, которые можно помещать в него или извлекать. В нашем случае ширина стека равна двум байтам или 16 битам. Регистр SP (указатель стека) содержит адрес последнего добавленного элемента. Этот адрес также называется вершиной стека. Противоположный конец стека называется дном 🙂
Дно стека находится в верхних адресах памяти. При добавлении новых элементов в стек значение регистра SP уменьшается, то есть стек растёт в сторону младших адресов. Как вы помните, для COM-программ данные, код и стек находятся в одном и том же сегменте, поэтому если постараться, стек может разрастись и затереть часть данных и кода (надеюсь, с вами такой беды не случится :)).
Для стека существуют всего две основные операции:
Добавление элемента в стек
Выполняется командой PUSH. У этой команды один операнд, который может быть непосредственным значением, 16-битным регистром (в том числе сегментым) или 16-битной переменной в памяти. Команда работает следующим образом:
Существуют ещё 2 команды для добавления в стек. Команда PUSHF помещает в стек содержимое регистра флагов. Команда PUSHA помещает в стек содержимое всех регистров общего назначения в следующем порядке: АХ, СХ, DX, ВХ, SP, BP, SI, DI (значение DI будет на вершине стека). Значение SP помещается то, которое было до выполнения команды. Обе эти команды не имеют операндов.
Извлечение элемента из стека
Выполняется командой POP. У этой команды также один операнд, который может быть 16-битным регистром (в том числе сегментым, но кроме CS) или 16-битной переменной в памяти. Команда работает следующим образом:
Обратите внимание, что извлеченный из стека элемент не обнуляется и не затирается в памяти, а просто остаётся как мусор. Он будет перезаписан при помещении нового значения в стек.
pop cx ;Поместить значение из стека в CX pop es ;Поместить значение из стека в ES pop [x] ;Поместить значение из стека в переменную x pop word [di] ;Поместить значение из стека в слово по адресу в DI
Соответственно, есть ещё 2 команды. POPF помещает значение с вершины стека в регистр флагов. POPA восстанавливает из стека все регистры общего назначения (но при этом значение для SP игнорируется).
Пример программы
Имеется двумерный массив — таблица 16-битных значений со знаком размером n строк на m столбцов. Программа вычисляет сумму элементов каждой строки и сохраняет результат в массиве sum. Первый элемент массива будет содержать сумму элементов первой строки, второй элемент — сумму элементов второй строки и так далее.
Как видите, в программе два вложенных цикла: внешний и внутренний. Внешний цикл — это цикл по строкам таблицы. Внутренний цикл вычисляет сумму элементов строки. Стек здесь используется для временного хранения счётчика внешнего цикла. Перед началом внутреннего цикла CX сохраняется в стеке, а после завершения восстанавливается. Такой приём можно использовать для программирования и большего количества вложенных циклов.
Turbo Debugger
В отладчике Turbo Debugger стек отображается в нижней правой области окна CPU. Левый столбец чисел — адреса, правый — данные. Треугольник указывает на вершину стека, то есть на тот адрес, который содержится в регистре SP. Если запустить программу в отладчике, то можно увидеть, как работают команды «push cx» и «pop cx».
Упражнение
Объявите в программе строку «$!olleH». Напишите код для переворачивания строки с использованием стека (в цикле поместите каждый символ в стек, а затем извлеките в обратном порядке). Выведите полученную строку на экран. Свои результаты пишите в комментариях 🙂