Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование на языке Ассемблер. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
11.08.2026
Размер:
603 Кб
Скачать

8. ЦИКЛ

Выполнение цикла в Ассемблере можно организовать с помощью нескольких команд. Одна из таких команд – это команда LOOP. Команда цикла в Ассемблере всегда уменьшает значение счётчика на единицу. Это значение находится в регистре СХ (или ECX). Отличия между командами цикла заключаются только в условиях, при которых выполняется переход к метке или цикл завершается.

Команда LOOP выполняет переход к метке во всех случаях, когда значение в регистре СХ не равно нулю. Чтобы организовать цикл с помощью этой команды, сначала необходимо в регистр СХ записать число итераций цикла (т. е. сколько раз цикл должен быть выполнен), затем вставить в код метку, а затем написать команды, которые должны быть выполнены в цикле. А уже в конце списка этих команд записать команду

LOOP.

8.1. Инструкция LOOP

Инструкция LOOP в Ассемблере уменьшает значение в регистре СХ в реальном режиме или ECX в защищённом. Если после этого значение в СХ не равно нулю, то команда LOOP выполняет переход на МЕТКУ. Синтаксис команды:

LOOP МЕТКА

Алгоритм работы команды LOOP:

CX = CX – 1;

если CX не равен 0, то выполнить переход;

иначе не выполнять переход, продолжить цикл.

То есть команда LOOP выполняется в два этапа. Сначала из регистра СХ вычитается единица и его значение сравнивается с нулём. Если регистр не равен нулю, то выполняется переход к указанной МЕТКЕ. Иначе переход не выполняется,

41

и управление передаётся команде, которая следует сразу после команды LOOP.

Более понятно это будет в примере программы, представленном далее.

Пример программы:

.model tiny

 

 

.code

 

 

ORG 100h

 

 

start:

 

 

MOV CX, 26

; цикл будет выполнен 26 раз

MOV DL, 'A'

;

CL=41h (ASCII-код) – первая буква

MOV BX, 676h

;

позиция первой буквы на экране

MOV AX, 0B800h

; установить AX=B800h (память VGA)

MOV DS, AX

; копировать значение из AX в DS

MOV DH, 01001110b ; CH=атрибуты цвета

abcde:

 

 

MOV [BX], DX

; записать символ в видеопамять

INC DL ; увеличить ASCII-код (для следующего сим-

вола)

 

 

ADD BX, 2

; сместить координаты

LOOP abcde

; повторить цикл

END start

 

 

42

9. ПРОЦЕДУРЫ

Процедура – это часть кода, которая может быть вызвана из программы для выполнения какой-либо определенной задачи. Процедуры делают программу более структурной и доступной для понимания. В общем случае процедура возвращает программу к той же самой точке, откуда она была вызвана.

Синтаксис для объявления процедуры:

имя PROC

;здесь находится

;код процедуры ...

RET

имя ENDP

имя – это имя процедуры. Одно и то же имя должно быть в верхней и нижней части, совпадение имен используется для проверки правильности закрытия процедур.

Обычно команда RET используется для возвращения в операционную систему. Эта же команда используется для возвращения из процедуры (фактически операционная система воспринимает программу как специальную процедуру).

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

Команда CALL используется для вызова процедуры. Пример:

ORG 100h CALL m1 MOV AX, 2

RET ; вернуться в операционную систему m1 PROC

MOV BX, 5

RET ; вернуться в программу, из которой была вызвана m1 ENDP

END

43

Вышеописанный пример вызывает процедуру m1, которая выполняет команду MOV BX, 5. После окончания процедуры программа выполняет команду, следующую после команды CALL, т. е. команду: MOV AX, 2.

Есть несколько способов передачи параметров процедуре. Самый простой из них – использование регистров. Здесь представлен пример процедуры, которая принимает два параметра в регистрах AL и BL, умножает их и возвращает результат в регистр AX:

ORG 100h

MOV AL, 1

MOV BL, 2

CALL m2

CALL m2

 

CALL m2

 

CALL m2

 

RET

; вернуться в операционную систему

m2 PROC

 

MUL BL

; AX=AL*BL

RET

; вернуться в вызвавшую программу

m2 ENDP

 

END

 

В этом примере значение регистра AL изменяется каждый раз при вызове процедуры, а состояние регистра BL не изменяется. Таким образом, был получен алгоритм вычисления числа 2 в 4 степени. В результате в регистре AX будет число 16 (или 10h).

Далее представлен пример, в котором используется процедура для вывода на экран сообщения «Hello World!»:

ORG 100h

LEA SI, msg ; загрузить адрес msg в регистр SI CALL print_me

RET ; вернуться в операционную систему

44

;эта процедура печатает строку, строка должна

;оканчиваться нулем (иметь ноль в конце),

;адрес строки должен быть в регистре SI: print_me PROC

next_char:

CMP b.[SI], 0 ; проверить регистр SI, если он

JE stop

 

;

равен 0, перейти к метке stop

MOV AL,

[SI]

;

иначе получить ASCII-символ

MOV AH,

0Eh

; номер функции для печати символа

INT 10h

;

используем прерывание для печати

 

;

символа из AL

ADD SI,

1 ; увеличить индекс строкового массива

JMP next_char ;

вернуться и напечатать

stop:

 

;

другой символ

 

 

 

RET

 

;

вернуться в программу

print_me

ENDP

 

 

;msg DB 'Hello World!', 0

;строка с нулевым окончанием

END

Префикс «b.» перед [SI] означает, что необходимо сравнивать байты, а не слова. Если необходимо сравнить слова, то нужно добавить префикс «w.» вместо «b.». Если один из сравниваемых операторов – регистр, то вставлять префиксы не требуется, так как компилятор знает размер каждого регистра.

45

10. СТЕК

Стек – это область памяти для хранения временных данных. Стек используется командой CALL для хранения адреса, чтобы программа могла вернуться к тому месту, откуда была вызвана процедура. Команда RET получает этот адрес из стека и возвращает управление по этому смещению. То же самое происходит, когда команда INT вызывает прерывание, она записывает в стек регистр флагов, сегмент и смещение кода. Команда IRET используется для возвращения после вызова прерывания.

Стек можно использовать для хранения любых данных. Для работы со стеком имеются две команды:

PUSH – записывает 16-битовое значение в стек;

POP – получает 16-битовое значение из стека.

Синтаксис для команды PUSH:

PUSH REG

PUSH SREG

PUSH memory

PUSH immediate

Здесь:

REG: AX, BX, CX, DX, DI, SI, BP, SP; SREG: DS, ES, SS, CS;

memory: [BX], [BX+SI+7], 16-битовая переменная и т. п.; immediate: 5, -24, 3Fh, 10001101b, и т. п.

Синтаксис для команды POP:

POP REG

POP SREG

POP memory

Здесь:

REG: AX, BX, CX, DX, DI, SI, BP, SP; SREG: DS, ES, SS, (кроме CS).

memory: [BX], [BX+SI+7], 16-битовая переменная и т. п.

46

Команды PUSH и POP работают только с 16-битовыми значениями.

PUSH immediate работает только на процессорах 80186 и выше.

Стек использует алгоритм LIFO (Last In First Out – последним пришел – первым ушел), это значит, что если поместить значения одно за другим в стек: 1, 2, 3, 4, 5, то первое значение, которое можно будет получить из стека, будет 5, затем 4, 3, 2 и только потом 1 (рис. 8).

Рис. 8. Запись и получение значений из стека

Очень важно применять равное количество команд PUSH и POP, иначе стек может быть нарушен и невозможно будет вернуться в операционную систему. Для возвращения в операционную систему используется команда RET. Когда программа запускается, ее адрес записывается в стек (обычно это

0000h).

Команды PUSH и POP чрезвычайно полезны, т. к. для хранения данных обычно недостаточно только регистров. Вот выход из ситуации:

47

­записать значение регистра в стек (используя PUSH);

­использовать этот регистр в своих целях;

­восстановить предыдущее значение регистра из стека (используя POP).

Пример:

ORG 100h

MOV AX, 1234h

PUSH AX ; записать значение из AX в стек

MOV AX, 5678h ; изменить значение регистра AX POP AX ; восстановить первоначальное значение AX RET

END

Стек можно также использовать для того, чтобы поменять местами значения в регистрах:

ORG 100h

MOV AX, 1212h ; записать в АХ число 1212h. MOV BX, 3434h ; записать в ВХ число 3434h. PUSH AX ; записать значение AX в стек.

PUSH BX ; записать значение ВX в стек. POP AX ; установить в AX значение BX. POP BX ; установить в ВX значение АX. RET

END

Обмен данными происходит потому, что стек использует алгоритм LIFO (последним пришел – первым вышел), поэтому когда в стек помещаем число 1212h, а затем – 3434h, то при обращении к стеку сначала будет получено число 3434h, и только потом – 1212h.

Область памяти стека устанавливается при помощи ре-

гистров SS (Stack Segment – сегмент стека) и SP (Stack Pointer

– указатель стека). Обычно операционная система устанавливает значения этих регистров на начало программы.

Команда PUSH источник делает следующее:

48

вычитает 2 из регистра SP;

записывает значение источника по адресу SS:SP. Команда POP приемник делает следующее:

­записывает данные, размещенные по адресу SS:SP

вприемник;

­увеличивает на 2 значение регистра SP.

Текущий адрес указателя в SS:SP называется вершиной стека.

Для COM-файлов сегмент стека – это обычно и сегмент кода, а указатель стека установлен в значение 0FFFEh. По адресу SS:0FFFEh записывается адрес возврата для команды RET, которая выполняется в конце программы.

Вы можете наблюдать за работой стека, щелкнув по кнопке [Stack] в окне эмулятора. Вершина стека отмечена знаком «<».

49

11. МАКРОСЫ

Макросы – это те же процедуры, только виртуальные. Макросы подобны процедурам, но они выполняются только во время компиляции. После компиляции все макросы заменяются реальными командами. Если макрос будет объявлен, и никогда не будет использоваться в коде, компилятор просто проигнорирует его. Emu8086.inc содержит хорошие примеры использования макросов. Этот файл содержит несколько макросов, которые делают создание кода более легким.

Определение макроса:

имя MACRO [параметры,...] <команды>

ENDM

В отличие от процедур, макрос должен быть определен перед участком кода, где он будет использоваться, например:

MyMacro MACRO p1, p2, p3 MOV AX, p1

MOV BX, p2 MOV CX, p3 ENDM

ORG 100h MyMacro 1, 2, 3 MyMacro 4, 5, DX RET

Вышеописанный код эквивалентен следующему набору команд:

MOV AX, 00001h

MOV BX, 00002h

MOV CX, 00003h

MOV AX, 00004h

MOV BX, 00005h MOV CX, DX

50

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]