
ПСМПС (лаб)
.pdf41
стве нулю регистра ECX/CX управление передается на инструкцию, следующую после LOOP, а в противном случае управление передается на метку перехода. Шаблон такого цикла имеет вид:
mov CX, <число итераций> L:
; тело цикла loop L
Инструкции LOOPE/LOOPZ завершают цикл в случае, если ECX/CX = 0 и флаг ZF = 1; если ECX/CX > 0 или флаг ZF = 0 управление передается на метку перехода:
loope <jump address> loopz <jump address>
Инструкции LOOPNE/LOOPNZ являются обратными инструкциям LOOPE/LOOPZ завершает цикл в случае, если ECX/CX = 0 и флаг ZF = 0; если ECX/CX > 0 или флаг ZF = 1, управление передается на метку перехода:
loopne <jump address> loopnz <jump address>
Инструкции типа LOOPE/LOOPZ по принципу своей работы расширяют действие команды LOOP тем, что дополнительно анализируют флаг ZF, что дает возможность организовать досрочный выход из цикла, используя этот флаг. Типичное использование данных команд связано с операцией поиска определенного значения в последовательности ячеек памяти, при которой выполняется сравнение двух чисел.
3.Практическая часть
3.1.Организация циклической структуры с использованием инструкций условных переходов
Рассмотрим программу, которая осуществляет циклический вывод на экран строки сообщения.
42
1. Создадим проект и введем следующий исходный код:
text segment |
'code' |
|
assume |
CS: text, DS: data |
|
begin: |
mov AX, data |
|
mov |
DS, AX |
|
mov |
CX, 10 |
|
mov |
DX, offset message |
|
k1: |
int 21h |
|
dec CX |
; CX = CX - 1 |
|
cmp CX,0 |
; если не 0 |
|
jne |
k1 |
; переход на k1 |
mov |
AH, 4ch |
|
mov |
AL, 00h |
|
int |
21h |
|
text ends |
|
|
data segment |
|
|
message |
‘Сообщение’, 10, 13, ‘$’ |
|
data ends |
|
|
end |
begin |
2. Произведем сборку проекта с отладочной информацией и выполним пошаговую отладку в среде CodeView. В ходе отладки необходимо наблюдать за содержимым регистра IP.
3.2. Условные переходы и анализ данных
Следующая программа иллюстрирует применение инструкций условного перехода для организации сложных алгоритмов анализа данных на примере ввода и проверки пароля.
1. Создадим проект и введем следующий исходный код (используется синтаксис IDEAL):
.model small
.data
|
43 |
prompt |
db 'Enter password>> $' |
message |
db 10, 13, ' Program had accepted the password!$' |
string |
db 80 dup(?) |
password |
db 'terribleblow' |
enter |
db 10, 13, '$' |
star |
db '*$' |
.code
; ---Инициализация сегментного регистра---
mov AX, @data mov DS, AX
;---Выведем приглашение---
begin:
mov AH, 09h
mov DX, offset prompt int 21h
;---Ввод пароля с клавиатуры---
;Настроим индекс элемента строки mov BX, 0
;Вызываем функцию ввода символа с клавиатуры без эха
pass:
mov AH, 08h int 21h
;Сравниваем с кодом номер 13 (возврат каретки)
cmp AL, 13
;Если равно, начинаем сравнивать je compare
;Показываем "звёздочки" вместо букв push AX
mov AH, 09h
mov DX, offset star int 21h
pop AX
;Если нет, продолжаем заполнять массив строки
44
mov string[BX], AL
; Увеличиваем значение индекса inc BX
;Следующий символ jmp pass
;---Сравниваем строки---
;Настраиваем сегментный регистр ES
;Перед началом сравнения проверим
;наличие хотя бы одного символа compare:
cmp BX, 0 je err
;Обмениваем значения сегментных регистров push DS
pop ES
;Настраиваем индексные регистры на смещение строк mov SI, offset string
mov DI, offset password
;Сбрасываем флаг переноса
cld
;Число циклов сравнений mov CX, BX
;Цикл сравнений символов в строке repe cmpsb
;---Если неверно переходим на метку---
jne err
;---Выведем приглашение---
mov AH, 09h
mov DX, offset message int 21h
; ---Корректно выйдем из программы---
mov AX, 4C00h int 21h
45
; ---Перевод строки и переход на начало---
err:
mov AH, 09h
mov DX, offset enter int 21h
jmp begin
end
2. Выполним сборку и пошаговую отладку проекта с наблюдением за содержимым регистров процессора.
3.3.Использование инструкции цикла
Вследующей программе создается массив символов, начиная с символа «пробела» и заканчивая символом «подчёркивания», и выводится сформированный массив.
1.Создадим проект и введем следующий исходный код (используется синтаксис MASM):
array segment 'code'
assume CS:array, DS: array
begin:
; Инициализация сегментного регистра данных mov AX, array
mov DS, AX
mov CX, 64 |
; число повторений |
цикла |
|||
mov |
AL, ' ' |
; |
первый |
символ массива |
|
mov |
SI,0 |
; |
индекс |
источника |
в "нуль" |
;Начиаем цикл помещая первый символ в массив
fill:
mov mes[SI], AL
;Увеличиваем значение счетчика и код символа inc SI
inc AL
46
loop fill
; Выведем строку на экран mov AH, 40h
mov BX, 1 mov CX, 64
mov DX, offset mes int 21h
mov AH, 4Ch mov AL, 00h int 21h
mes db 64 dup('~'); array ends
end begin
3.4.Задания
1.Изучить материал теоретических разделов 2.1-2.3.
2.Выполнить примеры из разделов 3.1-3.3
3.Разработать программу на языке ассемблера реализующую алгоритм копирования символов из одного массива в другой.
4.Контрольные вопросы
1.Содержимое каких регистров изменяется при выполнении инструкций передачи управления?
2.Дайте классификацию команд передачи управления.
3.Чем отличаются прямой и косвенный безусловные переходы?
47
Лабораторная работа № 4
СТРУКТУРНОЕ И МОДУЛЬНОЕ ПРОГРАММИРОВАНИЕ В АССЕМБЛЕРЕ. ПРОЦЕДУРЫ И МАКРОСРЕДСТВА
1. Цель работы
Целью данной лабораторной работы является рассмотрение вопросов, связанных с методами и средствами процедурного программирования в ассемблере. Предполагается изучение студентами семантических и синтаксических особенностей организации подпрограмм и макрокоманд в ассемблере, а также методов взаимодействия подпрограмм. Одной из главных задач этой работы является освоение студентами общих базовых принципов процедурного программирования на примере языка ассемблера, что создает предпосылки для более глубокого понимания процедурно-ориентированной парадигмы и тесной взаимосвязи машинного кода с конструкциями высокоуровневых языков программирования.
2. Основные теоретические сведения
При решении сложных задач программирования с использованием ассемблера разработчик сталкивается с тем, что в программе появляется большое число повторяющихся участков кода. При этом затрудняется чтение текста программы, снижается ее наглядность. В языке ассемблера есть несколько средств, решающих проблему дублирования участков программного кода.
Кэтим средствам относятся процедуры и макрокоманды.
2.1. Введение в процедурное программирование в ассемблере
2.1.1. Общие сведения о подпрограммах. Процедуры в ассемблере
Большинство реальных программ по своему объему намного превышают рассмотренные ранее примеры на основе использования базовых алгоритмических структур. Объем средней программы даже в развитых высокоуровневых языках программирования составляет десятки тысяч строк кода.
Поэтому все языки программирования, и ассемблер здесь не является исключением, имеют средства структурирования программ, т.е. представления их исходного кода в виде логически связанных структурных единиц, ка-

48
ждая из которых призвана решать специфическую, сравнительно небольшую алгоритмическую задачу. Такими средствами в языках программирования являются подпрограммы (subroutines), которые в общем случае бывают двух видов процедуры (procedures) и функции (functions).
Подпрограммой называется особым образом оформленный фрагмент программы, имеющий собственное имя, т.е. идентификатор. Упоминание этого имени в тексте программы приводит к активизации подпрограммы и называется ее вызовом (call). Сразу после активизации подпрограммы начинают выполняться входящие в нее операторы (команды), а после выполнения последнего из них управление возвращается обратно в основную программу и продолжают выполняются операторы, стоящие непосредственно за оператором вызова процедуры (рис. 4.1).
Вызывающая подпрограмма или основная программа
вызов
продолжение основной подпрограммы
Вызываемая подпрограмма
Рис. 4.1. Схема вызова подпрограммы
Для обмена информацией между основной программой и подпрограммой используется один или несколько параметров вызова (call parameters). Параметр представляет собой некоторое значение, которое передается в подпрограмму и используется в ней посредством ее идентификатора.
Вклассическом понимании, более свойственном для высокоуровневых языков программирования, процедура – это подпрограмма, которая не возвращает какое-либо значение и применяется для выполнения каких-либо действий. Для передачи значения обратно в вызывающую подпрограмму в процедуре может использоваться специальные параметры. Напротив, функция, подобна переменной, может участвовать в выражениях и возвращает слева от себя некоторое значение заданного в ее определении типа.
Вассемблере отсутствует понятие «функциями», и любая подпрограмма является процедурой.
49
Процедура в ассемблере представляет собой именованную группу команд для решения конкретной задачи, которая обладает средствами получения управления из точки вызова задачи более высокого уровня и возврата управления в эту точку. При этом ассемблер не накладывает на процедуры никаких ограничений – на любой адрес программы можно передать управление командой вызова процедуры CALL, и оно вернется к вызвавшей процедуре, как только встретится команда RET. Такая свобода выражения легко может приводить к трудночитаемым программам, и в язык ассемблера были включены директивы логического оформления процедур.
Процедура в ассемблере оформляется следующим образом:
<label> |
proc |
[<type> <lang> uses <registers>] |
|
... |
|
|
ret |
|
<label> |
endp |
|
Все операнды директивы PROC необязательны. Операнд <type> может принимать значения NEAR и FAR, и если он указан, все команды RET в теле процедуры будут заменены соответственно на RETN и RETF. По умолчанию подразумевается, что процедура имеет тип NEAR. Операнд <lang> определяет взаимодействие процедуры с языками высокого уровня. В некоторых ассемблерах директива PROC позволяет также считать параметры, передаваемые вызывающей программой. В этом случае указание языка необходимо, так как различные языки высокого уровня используют разные способы передачи параметров. За директивой USES должен быть указан список регистров, значения которых изменяет процедура. При указании этого операнда ассемблер помещает в начало процедуры набор команд PUSH, а перед командой RET – набор команд POP, так что значения перечисленных регистров будут сначала сохранены в стеке, а затем восстановлены.
Процедура может размещаться в любом месте программы, но так, чтобы на нее не было случайным образом передано управление. Это связано с тем, что процедура в ассемблере трактуется как обыкновенная метка. В большинстве случаев реального применения используются следующие варианты размещения процедуры в программе:
•в начале программы (до первой исполняемой команды);
•в конце (после команды, возвращающей управление операционной системе);
•в другом модуле.
50
Размещение процедуры в начале сегмента кода предполагает, что последовательность команд, ограниченная парой директив PROC и ENDP, будет размещена до метки, обозначающей первую команду, с которой начинается выполнение программы. Эта метка должна быть указана как параметр директивы END, обозначающей конец программы:
. . .
showmessage proc push AX push DX
mov AH, 09h
mov DX, offset message int 21h
pop DX pop AX ret
showmessage endp
main proc
. . .
call showmessage
. . .
mov AX, 4C00h int 21
endp end main
. . .
Размещение процедуры в конце программы предполагает, что последовательность команд, ограниченная директивами PROC и ENDP, будет размещена после команды, возвращающей управление операционной системе:
main proc
. . .
call showmessage
. . .
mov AX, 4C00h int 21
endp