Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Разработка приложений на языке ассемблер в среде MS MASM. Учебно-методическое пособие по дисциплинам «Ассемблер» и «Операционные системы и системное п
.pdf
Команда PUSH заносит в стек четырех- или двухбайтовый операнд,
изменяя при этом текущее значение указателя стека ESP.
Если операнд четырехбайтовый (m32,r32), команда PUSH умень-
шает значение указателя стека ESP на 4 и записывает в стек 4 байта данных по адресу ESP:
ESP:=ESP-4, [SS:ESP]:=операнд
Запись [SS:ESP] означает: "содержимое памяти по адресу SS:ESP".
Если операнд двухбайтовый (m16,r16) команда PUSH уменьшает текущее значение указателя стека ESP на 2 и записывает в стек 2
байта данных по адресу ESP:
ESP:=ESP-2, [SS:ESP]:=операнд
Пример:
.STACK 40 ;ESP:=40
...
PUSH EAX ;ESP:=40-4=36
PUSH DS ;ESP:=36-2=34
6.2.2 Чтение из стека
POP операнд
Команда POP извлекает из стека четырех- или двухбайтовое содержимое по адресу ESP , копирует извлеченное значение в указанный операнд и формирует новое значение указателя стека.
Если операнд четырехбайтовый (m32,r32), команда POP считывает из стека по адресу ESP 4 байта данных и помещает их в операнд, а
затем увеличивает значение указателя стека ESP на 4:
операнд:=[SS:ESP], ESP:=ESP+4,
Если операнд двухбайтовый (m16,r16) команда POP считывает
из стека по адресу ESP 2 байта данных и помещает их в операнд, а затем
увеличивает значение указателя стека ESP на 2:
операнд:=[SS:ESP], ESP:=ESP+2.
Пример:
.STACK 40
...
MOV EAX,12345678H
MOV BX,0020H
;Cодержимое стека:
PUSH EAX ;ESP:=40-4=36 |xxxxxxxx|12345678|
PUSH BX ;ESP:=36-2=34 |xxxx0020|12345678|
POP DX ;DX:=0020H, |xxxx0020|12345678|
;ESP:=34+2=36
51

POP EBX ;EBX:=12345678H, |xxxx0020|12345678|
;ESP:=36+4=40
6.3 Создание и использование процедур
6.3.1 Описание процедуры
Для описания процедуры используются директивы PROC и ENDP:
имя_процедуры PROC
<тело процедуры>
ENDP
Директива PROC служит для определения имени процедуры и записывается в ее начале. Имя процедуры выполняет роль метки, и использу-
ется для безусловного перехода на выполнение кода процедуры при вызове
командой CALL. Имя процедуры определяет программист. Тело процедуры
состоит из последовательности произвольных команд ассемблера, составляющих код процедуры. В последней строке процедуры записывается директива ENDP.
Процедура может быть описана:
- между директивой .code и точкой входа (меткой main:);
- после последней команды сегмента кода;
- в другом модуле (исходном файле).
6.3.2 Вызов процедуры
CALL имя_процедуры
Команда CALL производит безусловный переход на метку
имя_процедуры, предварительно сохраняя в стеке текущее значение указателя команд EIP. Вспомним, что EIP всегда содержит адрес следующей команды. В данном случае это будет адрес команды, следующей за ко-
мандой CALL. Его называют адресом возврата из процедуры, поскольку
после завершения процедуры должен быть обеспечен автоматический пе-
реход по этому адресу.
6.3.3 Возврат из процедуры
RET [число]
Команда RET извлекает из стека адрес возврата, ранее записанный
туда командой CALL, и обеспечивает автоматическое продолжение выпол-
нения программы, начиная с команды, следующей за командой CALL. Необязательный параметр число позволяет дополнительно удалить из стека
заданное количество байт (обычно используется для очистки стека от раз-
мещенных в нем параметров процедуры).
52

а2
EBP
+12
EBP+8
EBP
а1
. . .
а3
EIP
EBP
6.4 Организация интерфейса с процедурой
Как правило, процедура предполагает использование параметров
(входных и/или выходных). Описание процедуры с параметрами на языках
программирования высокого уровня выглядит примерно так:
Procedure myproc(a1,a2,a3),
где myproc - имя процедуры, a1,a2,a3 – формальные параметры.
При вызове такой процедуры потребуется указать фактические значения
параметров, например, так: myproc(5,8,0)
Программисту на ассемблере необходимо позаботиться о способе передачи параметров процедуре от основной (вызывающей) программы. Существует несколько таких способов, но наиболее универсальным является
метод передачи параметров через стек. Этот метод применяется в том числе
при передаче параметров в программах, написанных на языках высокого
уровня. Общий сценарий такого метода выглядит так. Вызывающая программа вначале последовательно записывает значения параметров процедуры в стек (с помощью команды PUSH), а затем производит вызов проце-
дуры (CALL). Процедура, получив управление, извлекает значения параметров из стека, обрабатывает их, перед завершением работы очищает стек
и производит возврат в основную программу (RET).
Рассмотрим, как реализуется такой сценарий применительно к вызову
процедуры myproc с тремя параметрами a1,a2,a3 типа DWORD (4
байта) (рисунок 6.3).
Вызывающая программа Процедура
;Вызов процедуры
PUSH A1
PUSH A2
PUSH A3
CALL PR_FUNC
ESP
2
ESP
1
EBP+16
MYPROC PROС
PUSH EBP
MOV EBP,ESP ; EBP:=ESP
. . .
MOV EAX,[EBP+8] ; EAX:=a3
MOV EBX,[EBP+12] ; EBX:=a2
MOV ECX,[EBP+16] ; ECX:=a1
. . .
POP EBP ;убрать EBP из стека
RET 12 ;убрать параметры
ENDP
Рисунок 6.3 - Использование стека при передаче параметров
53

Вызывающая программа вначале записывает параметры a1,a2,a3 в
стек с помощью команды PUSH. Расположение параметров в стеке показано на рисунке. Команда вызова процедуры CALL помещает в стек теку-
щее значение регистра EIP и передает управление процедуре. В этот момент текущее значение указателя стека ESP=ESP
.
1
Теперь процедуре необходимо извлечь значения параметров из стека.
Однако сделать это с помощью команды POP невозможно, поскольку на
вершине стека находится сохраненный ранее адрес возврата - регистр EIP!
Метод извлечения параметров из стека основан на использовании регистра
EBP, называемого альтернативным указателем стека. В начале значение
EBP спасается в стеке (на всякий случай), а потом в EBP помещается
текущее значение указателя стека ESP, равное теперь ESP2. Теперь "добраться" до значений параметров можно с помощью обычной команды
MOV! Например, значение параметра a3 находится в стеке по смещению
8 относительно сформированного значения регистра EBP, поэтому команда MOV EAX,[EBP+8] помещает в регистр EAX значение параметра
a3. Аналогично поступают с параметрами a2 (смещение EBP+12) и
a3 (смещение EBP+16).
При завершении процедуры следует вначале убрать из стека EBP
(возможно, и другие данные, если вы их туда положили), чтобы на вершине
оказался регистр EIP - адрес возврата из процедуры. Последняя команда
процедуры RET 12 возвращает управление основной программе (по ранее сохраненному значению указателя команд EIP) и "очищает" стек на
12 байт, освобождая его от сохраненных ранее параметров процедуры пу-
тем прибавления к ESP числа 12.
6.5 Рабочее задание
1. Преобразовать программу, разработанную в лабораторной работе
№4, представив в виде процедуры блок команд, предназначенных для обработки одной строки матрицы A по заданному алгоритму.
В качестве входных параметров процедуры использовать:
- адрес 1-го элемента обрабатываемой строки матрицы A;
- количество элементов в строке.
В качестве выходного параметра процедуры использовать значение,
рассчитанное при обработке строки в соответствии с заданным вариантом
(которое будет затем записано в массив B в основной программе).
Входные параметры процедуры передаются через стек, а выходной через регистр (см. пример программы в п. 6.8).
2. Проверить работоспособность программы и полученные на тесто-
вом примере результаты. Подготовить для отчета исходную матрицу А и
54

результирующий одномерный массив B в десятичном формате, а также в
виде скриншота дампа сегмента данных.
3. C помощью отладчика проследить за изменением состояния стека
в ходе выполнения программы, заполнив таблицу 6.1. В таблицу включить
только команды, воздействующие на стек, а также команды, использующие
регистры - указатели стека (ESP, EBP), в той последовательности, в какой
они встречаются в программе при обработке одной строки исходной матрицы. Значения регистров и содержимое стека фиксируются после выполнения соответствующей команды. Сделать скриншот сегмента стека после
выполнения последней команды записи в стек (PUSH) в процедуре так,
чтобы были видны все значения, записанные программой в стек.
6.6 Требования к отчету
1. Продемонстрируйте работающую программу преподавателю с
предъявлением результатов с помощью отладчика.
2. Подготовьте отчет, включающий:
- текст варианта задания;
- укрупненную блок-схему алгоритма разработанной программы;
- текст разработанной программы с обязательными комментариями;
- результаты работы программы в отладчике виде таблицы и в виде
скриншота (см. п. 2 рабочего задания);
- содержимое стека в виде заполненной таблицы 6.1 и скриншота сегмента стека в отладчике (см. п. 3 рабочего задания).
6.7 Контрольные вопросы
1. С какой целью используют процедуры?
2. Что такое стек? Поясните работу команд PUSH и POP.
3. Почему команду CALL называют "переход с возвратом"? Как обес-
печивается возврат в точку вызова?
4. Каков алгоритм выполнения команды RET.
5. Что произойдет при выполнении программы, приведенной в п. 6.8,
если в процедуре SUMSTR будет отсутствовать команда RET?
6. Можно ли передавать управление в процедуру с помощью команды
JMP? Что произойдет?
7. В стек последовательно были записаны двухбайтовые числа 1, 2, 3,
4, 5. Какое значение примет регистр EAX после выполнения следующих ко-
манд в указанной последовательности:
PUSH EAX POP EAX POP EBX POP ECX POP EAX
55

Таблица 6.1 – Состояние стека программы
Исходное
состояние стека
PUSH EBX PUSH EDX
...
MOV EBP,ESP
...
RET 8
ESP= ESP=
EBP= EBP= EBP= EBP=
ESP= EBP=
ESP=
ESP=
00000000
00000005
00000000
Примечания.
* Выделены ячейки стека с изменившимся содержимым
** В таблицу включить только команды, воздействующие на стек, а также команды, использующие регистры указатели стека (ESP,EBP), в той последовательности, в какой они встречаются в программе при одном вызове
процедуры. Значения регистров и содержимое стека фиксируются после выполнения соответствующей команды.
*** Для каждой включенной в таблицу команды указать наблюдаемые в отладчике значения регистров - указателей стека (ESP,EBP), размещая эти значения напротив ячеек стека, на которые эти регистры указывают.
56

57
6.8 Пример программы
;Дан двумерный массив А(4х5).
;Сформировать одномерный массив B(4),каждый элемент которого
;равен сумме элементов соответствующей строки массива A:
; B(i)=A(i,0)+A(i,1)+...+A(i,4) i=0,1,2,3
;Для суммирования элементов строки использовать процедуру
;==================================================
.486
.model flat, stdcall
option casemap: none
.stack 100h
;=========================================
include <\masm32\include\kernel32.inc>
includelib <\masm32\lib\kernel32.lib>
;=========================================
.data
N equ 4 ;количество строк
M equ 5 ;количество столбцов
A db 1,2,-3,-4,5
db -6,7,-8,9,0
db -1,3,2,-7,4
db 6,-8,2,0,1
B dw 4 dup (?)
;================================================================
.code
;=====Процедура==================================================
;Процедура вычисления суммы элементов одномерного массива байтов
;Входные параметры (передаются через стек):
; - адрес начала массива [ebp+8]
; - количество элементов массива [ebp+12]
;Выходной параметр (передается через регистр al - сумма элементов строки)
sumstr proc
push ebp ;спасаем ebp
mov ebp,esp ;формирование альтернативного указателя стека ebp=esp
push ecx ;спасаем используемые в процедуре регистры
push ebx
mov al,0 ;начальное значение суммы элементов строки (вых. параметр)
mov ecx,[ebp+8] ;извлечение из стека параметра 2 (число элементов массива)
mov ebx,[ebp+12];извлечение из стека параметра 1 (адреса массива)
;----------------next: ;начало цикла обработки массива
add al,[ebx] ;добавить к сумме (AL) текущий элемент массива
inc ebx ;установить bx на следующий элемент массива (+1)
loop next ;продолжить, если выбраны не все элементы массива
;---------------- pop ebx ;восстановить спасаемые регистры
pop ecx
pop ebp
ret 8 ;зачистить стек от параметров и возврат из процедуры
sumstr endp
;==== Основная программа===============================================
main:
;для адресации строк массива A используем регистр
; ebx - адрес (смещение) текущей строки
;для адресации элементов массива B используем регистр
; edi - смещение элемента
mov ecx,N ;счетчик строк массива A
mov edx,M ;количество элементов в строке

58
mov edi,0 ;смещение эл-та массива B
lea ebx,A ;адрес первой строки массива A
;----------------stroka: ;начало цикла по строкам А
;записываем в стек входные параметры для процедуры:
push ebx ;параметр 1: адрес выбранной строки массива A
push edx ;параметр 2: число элементов в строке массива A
call sumstr ;вызов процедуры суммирования
;---------------- cbw ;преобразование выходного параметра AL в двухбайтовый формат (AX)
mov B[edi],ax ;сумма - в массив B
add ebx,M*type A ;установить ebx на начало следующей строки A (+5)
add edi,type B ;установить edi на следующий элемент массива B(+2)
loop stroka ;продолжить, если обработаны не все строки
;---------------- invoke ExitProcess, 0 ;завершение программы c кодом 0
end main

59
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
1. Пильщиков В.Н. Программирование на языке ассемблера IBM PC.
– М.: Диалог-МИФИ, 2011
2. Пирогов В.Ю. Ассемблер для Windows. – СПб.: БХВ-Петербург,
2012
3. Справочник по макроассемблеру (MASM). Режим доступа:
https://docs.microsoft.com/ru-ru/cpp/assembler/masm/microsoft-macroassembler-reference?view=msvc-160
4. Встроенный ассемблер. Режим доступа:
https://docs.microsoft.com/ru-ru/cpp/assembler/inline/inlineassembler?view=msvc-160
5. Таненбаум Э., Остин Т. Архитектура компьютера. – СПб.: Питер,
2021

60
Учебно-методическое издание
ВАРФОЛОМЕЕВ ВИКТОР АРХИПОВИЧ
РАЗРАБОТКА ПРИЛОЖЕНИЙ НА ЯЗЫКЕ АССЕМБЛЕР В СРЕДЕ
MS MASM
Учебно-методическое пособие
Изд. № 93-21
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
