Ret ассемблер что это
19. Простые процедуры в ассемблер
Статья основана на материале xrnd с сайта asmworld (из учебного курса по программированию на ассемблер 16-битного процессора 8086 под DOS).
В этой части учебного курса мы рассмотрим основы создания процедур. Процедура представляет собой код, который может выполняться многократно и к которому можно обращаться из разных частей программы. Обычно процедуры предназначены для выполнения каких-то отдельных, законченных действий программы и поэтому их иногда называют подпрограммами. В других языках программирования процедуры могут называться функциями или методами, но по сути это всё одно и то же.
Команды CALL и RET
Ближние и дальние вызовы процедур
Существует 2 типа вызовов процедур. Ближним называется вызов процедуры, которая находится в текущем сегменте кода. Дальний вызов — это вызов процедуры в другом сегменте. Соответственно существуют 2 вида команды RET — для ближнего и дальнего возврата. Компилятор FASM автоматически определяет нужный тип машинной команды, поэтому в большинстве случаев не нужно об этом беспокоиться.
В учебном курсе мы будем использовать только ближние вызовы процедур.
Передача параметров
Очень часто возникает необходимость передать процедуре какие-либо параметры. Например, если вы пишете процедуру для вычисления суммы элементов массива, удобно в качестве параметров передавать ей адрес массива и его размер. В таком случае одну и ту же процедуру можно будет использовать для разных массивов в вашей программе. Самый простой способ передать параметры — это поместить их в регистры перед вызовом процедуры.
Возвращаемое значение
Кроме передачи параметров часто нужно получить какое-то значение из процедуры. Например, если процедура что-то вычисляет, хотелось бы получить результат вычисления. А если процедура что-то делает, то полезно узнать, завершилось действие успешно или возникла ошибка. Существуют разные способы возврата значения из процедуры, но самый часто используемый — это поместить значение в один из регистров. Обычно для этой цели используют регистры AL и AX. Хотя вы можете делать так, как вам больше нравится.
Сохранение регистров
Хорошим приёмом является сохранение регистров, которые процедура изменяет в ходе своего выполнения. Это позволяет вызывать процедуру из любой части кода и не беспокоиться, что значения в регистрах будут испорчены. Обычно регистры сохраняются в стеке с помощью команды PUSH, а перед возвратом из процедуры восстанавливаются командой POP. Естественно, восстанавливать их надо в обратном порядке. Примерно вот так:
Пример
Для рисования горизонтальной линии из символов предназначена процедура draw_line. В DL передаётся код символа, а в CX — количество символов, которое необходимо вывести на экран. Эта процедура не возвращает никакого значения. Для вывода 2-х символов конца строки написана процедура print_endline. Она вызывается без параметров и тоже не возвращает никакого значения. Коды символов для рисования рамок можно узнать с помощью таблицы символов кодировки 866 или можно воспользоваться стандартной программой Windows «Таблица символов», выбрав шрифт Terminal.
Результат работы программы выглядит вот так:
Отладчик Turbo Debugger
Небольшое замечание по поводу использования отладчика. В Turbo Debugger нажимайте F7 («Trace into»), чтобы перейти к коду вызываемой процедуры. При нажатии F8(«Step over») процедура будет выполнена сразу целиком.
Упражнение
Объявите в программе 2-3 массива слов без знака. Количество элементов каждого массива должно быть разным и храниться в отдельной 16-битной переменной без знака. Напишите процедуру для вычисления среднего арифметического массива чисел. В качестве параметров ей будет передаваться адрес массива и количество элементов, а возвращать она будет вычисленное значение. С помощью процедуры вычислите среднее арифметическое каждого массива и сохраните где-нибудь в памяти. Выводить числа на экран не нужно, этим мы займемся в следующей части.
RET Возврат из процедуры
RETF Возврат из дальней процедуры
Команда ret извлекает из стека адрес возврата и передает управление назад в программу, первоначально вызвавшую процедуру. Если командой ret завершается ближняя процедура, объявленная с атрибутом near, или используется модификация команды retn, со стека снимается одно слово- относительный адрес точки возврата. Передача управления в этом случае осуществляется в пределах одного программного сегмента. Если командой ret завершается дальняя процедура, объявленная с атрибутом far, или используется модификация команды retf, со стека снимаются два слова: смещение и сегментный адрес точки возврата. В этом случае передача управления может быть межсегментной.
В команду ret может быть включен необязательный операнд (кратный 2), который указывает, на сколько байтов дополнительно смещается указатель стека после возврата в вызывающую программу. Прибавляя эту константу к новому значению SP, команда ret обходит аргументы, помещенные в стек вызывающей программой (для передачи процедуре) перед выполнением команды call. Обе разновидности команды не воздействуют на флаги процессора.
push AX ;Параметр 1, передаваемый в
;подпрограмму
push SI ;Параметр 2, передаваемый в
;подпрограмму
call subr ;Вызов подпрограммы
…
subr proc near
…
;Извлечение из стека параметров
; (без изменения содержимого SP)
ret 4 ;Возврат в вызывающую
;программу и снятие со стека
;двух слов с параметрами
subr endp
RET Возврат из процедуры
RETF Возврат из дальней процедуры
Команда ret извлекает из стека адрес возврата и передает управление назад в программу, первоначально вызвавшую процедуру. Если командой ret завершается ближняя процедура, объявленная с атрибутом near, или используется модификация команды retn, со стека снимается одно слово- относительный адрес точки возврата. Передача управления в этом случае осуществляется в пределах одного программного сегмента. Если командой ret завершается дальняя процедура, объявленная с атрибутом far, или используется модификация команды retf, со стека снимаются два слова: смещение и сегментный адрес точки возврата. В этом случае передача управления может быть межсегментной.
В команду ret может быть включен необязательный операнд (кратный 2), который указывает, на сколько байтов дополнительно смещается указатель стека после возврата в вызывающую программу. Прибавляя эту константу к новому значению SP, команда ret обходит аргументы, помещенные в стек вызывающей программой (для передачи процедуре) перед выполнением команды call. Обе разновидности команды не воздействуют на флаги процессора.
push AX ;Параметр 1, передаваемый в
;подпрограмму
push SI ;Параметр 2, передаваемый в
;подпрограмму
call subr ;Вызов подпрограммы
…
subr proc near
…
;Извлечение из стека параметров
; (без изменения содержимого SP)
ret 4 ;Возврат в вызывающую
;программу и снятие со стека
;двух слов с параметрами
subr endp
MS-DOS и TASM 2.0. Часть 10. Команды ассемблера.
Команды ассемблера и команды процессора.
Стоит пояснить, что если к вопросу подойти формально строго, то команды процессора и команды ассемблера — это не одно и то же. Ассеммблер — хоть и низкоуровневый язык программирования, но иногда он без спроса программиста «корректирует код под себя». Причём у каждого ассемблера (masm, tasm, fasm) это может быть по-разному. Самый яркий пример — команда ret. В ассемблерном коде мы запишем ret, а реальный ассемблер ассемблирует её как retf или retn 8. Может также изменяться код, добавлением в качестве выравнивания кода команды процессора nop (об этом ниже в статье) и т.п. Чтобы не усложнять суть вопроса, под понятиями команды процессора и команды ассемблера мы будем подразумевать одно и то же.
Команды процессора (команды ассемблера) в большинстве своём работают с аргументами, которые в ассемблере называются операндами. Система машинного кода процессоров Intel содержит более 300 команд (команды процессора, сопроцессора, MMX-расширения, XMM-расширения). С каждым новым процессором их количество растёт. Для того, чтобы профессионально программировать, не надо зубрить и разбирать все команды процессора. При необходимости можно воспользоваться справочником. В процессе чтения статей, вы поймёте, что основная суть знания ассемблера состоит не в доскональном знании всех команд, а в понимании работы системы.
Не следует забывать, что команды процессор видит в виде цифр, которые можно рассматривать как данные. Например, команда NOP занимает один байт и её машинный код — 90h.
Начиная изучать язык низкого уровня, мы будем иметь дело с ограниченным набором старых-добрых команд процессора. Иные команды ассемблера понадобятся специалистам, заинтересованным в оптимизацией кода, связанного со сложными математическими расчетами данных большого объёма.
Основные (т.н. целочисленные) команды ассемблера позволяют написать практически любую программу для операционных систем MS-DOS и Windows. Количество команд ассемблера, которыми вы будете пользоваться будет расти со временем прохождения курса. Для более детального понимания, в последствии можете обратиться к справочнику команд.
Рассмотрим команды ассемблера на практическом примере.
С использованием среды разработки TASMED или любого текстового редактора набираем код. Программа, задаст вопрос на английском языке о половой принадлежности (имеется ввиду ваш биологический пол при рождении). Если вы нажмете m (Man), будет выведено приветствие с мужчиной, если w (Woman), то с женщиной, после этого программа прекратит работу. Если будет нажата любая другая клавиша, то программа предположит, что имеет дело с гоблином, не поверит и будет задавать вам вопросы о половой принадлежности, пока вы не ответите верно.
FE23 LOOCH DISASM
СПРАВОЧНИК ПО КОМАНДАМ
ПРОЦЕССОРОВ x86
Главная | Загрузка | Инструкция | Команды x86 | Карта сайта |
ПЕРЕДАЧИ УПРАВЛЕНИЯ |
Команда | Выполняемая операция |
JMP | Безусловная передача управления |
CALL | Вызов процедуры |
RET | Возврат из процедуры |
ENTER | Образование стекового кадра для параметров процедуры |
LEAVE | Отмена действия команды ENTER перед выходом из процедуры |
Команда безусловной передачи управления
Команда | Тип перехода | Операнд | Код | Формат | |
( 0 ) | JMP | SHORT | (rel8) | EB | |
( 1 ) | JMP | NEAR | (rel16) (rel32) | E9 | |
( 2 ) | JMP | NEAR, косвенный | (r/m16) (r/m32) | FF /100 | |
( 3 ) | JMP | FAR | (ptr16:16) (ptr16:32) | EA | |
( 4 ) | JMP | FAR, косвенный | (r/m16) (r/m32) | FF /101 |
В колонке «Операнд» показано, что является операндом для машинной команды. Размер операнда 16 бит или 32 бита определяется атрибутом размера операнда.
Вариант ( 0 ). Для короткого SHORT перехода смещение занимает всего 1 байт. Поэтому переход может быть только в пределах от (-128) до (+127).
Вариант ( 1 ). Для близкого NEAR перехода смещение занимает 2 байта или 4 байта. Если смещение из двух байт, переход возможен в пределах от (-32768) до (+32767).
Вариант ( 2 ). Переход косвенный. В команде задан не адрес перехода, а то место (регистр или место в памяти), где можно найти адрес перехода.
В первых трех вариантах переход происходит в пределах того же самого сегмента, в котором выполняется команда JMP. При таком переходе содержимое регистра CS не изменяется.
В следующих двух вариантах выполняется далекий FAR переход (межсегментный).
Вариант ( 3 ). В машинной команде задан полный указатель, то есть, задан сегмент и адрес в этом сегменте. В режие «16 бит» полный указатель (ptr16:16) занимет четыре байта, в режие «32 бита» полный указатель (ptr16:32) занимает шесть байт.
Вариант ( 4 ). Переход косвенный. В команде задан не адрес перехода, а то место, где можно найти полный указатель для перехода. Это место непременно должно быть в памяти, а не в регистре, так как в регистре полный указатель не помещается.
Команда вызова процедуры
Команда | Тип перехода | Операнд | Код | Формат | |
( 1 ) | CALL | NEAR | (rel16) (rel32) | E8 | |
( 2 ) | CALL | NEAR, косвенный | (r/m16) (r/m32) | FF /010 | |
( 3 ) | CALL | FAR | (ptr16:16) (ptr16:32) | 9A | |
( 4 ) | CALL | FAR, косвенный | (r/m16) (r/m32) | FF /011 |
Нетрудно заметить, что эта таблица для команды CALL очень похожа на предыдущую таблицу для команды JMP. Отличие только в том, что для команды CALL нет варианта с адресом типа SHORT.
Остальные четыре варианта полностью одинаковые для команды JMP и для команды CALL. Полностью совпадают типы перехода, типы операнда, формат машинной команды. (Понятно, что коды операций для команд JMP и CALL разные).
При далеких FAR вызовах процедуры в стеке сохраняется еще и значение сегмента CS, причем в стек сначала заносится CS, а затем EIP (или IP для режима «16 бит»).
Команда возврата из процедуры
Команда | Тип | Операнд | Код | Формат |
RET/RETN | NEAR | C3 | ||
RET/RETN | NEAR | (imm16) | C2 | |
RET/RETF | FAR | CB | ||
RET/RETF | FAR | (imm16) | CA |
Имеется четыре разных кода операции для команды RET, эти машинные команды выполняются немного по-разному.
При близком NEAR возврате из стека извлекается значение для EIP, при этом значение CS остается неизменным. При далеком FAR возврате из стека извлекается значение для EIP, а затем значение для CS.
На языке ассемблера у команды RET может быть необязательный численный параметр, который показывает, сколько байт (для режима «16 бит») или слов (для режима «32 бита») нужно дополнительно освободить в стеке после извлечения из стека адреса для возврата. Таким образом в стеке освобождается место, которое занимали параметры процедуры. (Обычно перед вызовом процедуры в стек заносятся значения параметров этой процедуры).
Команды стекового кадра
Команда ENTER ставится в начале процедуры. Она подготавливает стековый кадр к работе. Команда LEAVE ставится перед выходом из процедуры, причем ставится перед каждой командой RET, если выходов несколько. Она восстанавливает прежнюю ситуацию в стеке.
Команда ENTER имеет два параметра. Первый параметр задает количество байт, которое нужно зарезервировать для локальных переменных данной процедуры. Второй параметр обычно бывает равен нулю.
- Как называется мягкие кресла мешки
- Scotch напиток что это