Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Основы программирования на Ассемблере. Учебное пособие.pdf
Скачиваний:
0
Добавлен:
11.08.2026
Размер:
607 Кб
Скачать

Организация длинного цикла

mov CX,10000

; Счетчик цикла

fill:

; Метка начала цикла

...

; Тело длинного цикла

dec CX

; Декремент счетчика цикла

cmp CX,0

; Отработано заданное число шагов?

je finish

; Да, на метку продолжения программы

jmp fill

; Нет, на начало цикла

finish:

; Продолжение программы

Уменьшаем содержимое счетчика цикла и сравниваем полученное значение с 0. Если СХ = 0, это значит, что в цикле выполнено заданное число шагов, и командой условного перехода je осуществляется переход на продолжение программы (метка finish). Если СХ еще не равно нулю, командой безусловного перехода jmp осуществляется возврат в начало цикла. Команда jmp позволяет перейти в любую точку сегмента, и ограничение на размер тела цикла снимается.

Пример программы задержки выполнения

mov cx, 10 //внешний счетчик outer: push cx //сохраняем cx в стеке

mov cx, 0 //пересылаем в cx 0

tun: loop tun //цикл: вычитает 1, если не 0, то переход на метку pop cx //восстанавливает значение из стека cx

loop outer

7. ОБРАБОТКА СТРОК БАЙТОВ И СЛОВ

Для работы со строками или цепочками символов или чисел (с массивами произвольных данных) в МП предусмотрен ряд специальных команд:

movs – пересылка строки; cmps – сравнение двух строк;

sсas – поиск в строке заданного элемента;

lods – загрузка аккумулятора (регистров AL или АХ) из строки; stos – запись элемента строки из аккумулятора (регистров АХ или AL).

30

МП, выполняя эти команды, неявным образом использует ряд своих регистров. Только если все эти регистры настроены должным образом, команды будут выполняться правильно. Команды обработки строк, как правило, включаются в программу без явного указания операндов, однако каждая команда в действительности, использует два операнда. Операнды, находящиеся в памяти, всегда адресуются единообразно: операнд-источник через регистры DS:SI, а операндприемник через регистры ES:DI. При однократном выполнении команды обрабатывают только один элемент, а для обработки строки команды должны предваряться одним из префиксов повторения. В процессе обработки строки регистры SI и DI автоматически смещаются по строке вперед (если флаг DF = 0) или назад (флаг DF = 1), обеспечивая адресацию последующих элементов. Каждая команда имеет модификации для работы с байтами или словами (например, movsb и movsw). Для правильного выполнения команд обработки строк необходимо:

настроить регистры DS:SI и ES:DI;

установить или сбросить флаг DF;

занести в СХ длину обрабатываемой строки, а для команд sсas и stos еще поместить операнд-источник в регистр АХ (или AL при работе с байтами). Сама операция осуществляется одной командой, которая даже не содержит операндов, хотя может иметь префикс повторения REP. Стоит подчеркнуть, что строки, обрабатываемые рассматриваемыми командами, могут находиться в любом месте памяти. Например, с помощью команды movs можно скопировать массив данных из одной массивной переменной в другую, а можно переслать страницу текста на экран терминала. Рассмотрим несколько примеров использования команд обработки строк, ограничившись лишь теми фрагментами программ, которые имеют отношение к рассматриваемому вопросу.

Команды MOVS, STOS, CMPS и SCAS требуют инициализации ре-

гистра ES (обычно адресом в регистре DS).

Префикс REP обеспечивает повторение команды несколько раз. Префикс кодируется непосредственно перед цепочечной командой. Для использования префикса REP необходимо установить начальное значение в регистре CX. При выполнении цепочечной команды с префиксом REP происходит уменьшение на единицу значения в регистре CX до нуля. Таким образом, можно обрабатывать строки любой длины.

31

Флаг направления определяет направление повторяющейся операции:

для направления слева направо (увеличение адресов) необходимо с помощью команды CLD установить флаг DF в 0;

для направления справа налево необходимо с помощью команды STD установить флаг DF в 1.

7.1. ПЕРЕСЫЛКА И СРАВНЕНИЕ СТРОК Пример пересылки строки strl в строку text.

 

;Сегмент данных

 

 

 

strl

db ‘режим’

; исходная строка

 

strlen

equ $-strl

; длина строки в байтах

 

text

db 80 dup(‘ ’) ; приемная строка

 

;Сегмент команд

 

 

 

mov cx, strlen

; пересылаем количество байт в Сх

 

push DS

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

 

pop ES

 

; и настроить дополнительный сегмент

 

lea SI, strl

; адрес строки источника загрузили в SI

 

lea DI, text

; строка приёмник в DI

 

cld

 

; очистка флага, DF = 0

rep

movsb

; пересылаем строку побайтно; rep заставляет

МП выполнить movsb количество раз из Cx

 

возможна также запись rep

movs text, strl

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

REP – повторять операцию, пока CX не равно 0;

REPZ или REPE – повторять операцию, пока флаг ZF показывает «равно или ноль».

Прекратить операцию при флаге ZF, указывающему на «не равно

или не ноль», или при CX равном нулю;

REPNE или REPNZ – повторять операцию, пока флаг ZF показывает «не равно или не ноль».

Прекратить операцию при флаге ZF, указывающему на «равно или ноль», или при CX равном нулю.

32

Пример сравнения двух строк: в массиве text будем искать фраг-

мент stringlen.

;Сегмент данных

;адрес строки для сравнения

patt

db ‘stringlen’

pattlen egu $-patt

; длина

text

db 80 dup(?)

; приёмный буфер

;Сегмент команд

 

 

push

DS

 

 

pop

ES

 

 

lea

SI, patt

 

 

lea

DI, text

 

 

mov

cx, patlen

 

 

cld

 

 

repe

cmpsb

 

repe – префикс повторения. Заставляет МП выполнять сравнение последовательных байт-строк, пока эти байты равны. Повторения завершатся, если CX = 0 или встретились разные элементы ZF = 0.

7.2. ПОИСК В СТРОКЕ, ЗАГРУЗКА РЕГИСТРА ИЗ СТРОКИ, ЗАПИСЬ В СТРОКУ ИЗ РЕГИСТРА

scas – поиск в строке заданного символа

lods – загрузить в Ах из строки; строка по адресу DS: SI stos – запись элемента строки из Ах; строка по адресу ES: DI,

причем

строка источник в DS:SI

строка приёмник в ES:DI

Пример поиска в строке заданного символа < ! >:

str db 128 dup(?)

; исходная строка

;Сегмент команд

 

mov AL, ‘! ’

; в младшем байте у нас символ ‘!’

push DS pop ES lea DI, str

mov Cx, 128

33

repne scasb

; поиск пробела в строке и если символ найден,

 

 

то флагZF (нуля) установится в 1. Если символ не

 

 

найден, то флаг = 0. При этом просматривается

 

 

количество байтов из Сх.

Jne

not

; уход на метку, если не нашли (при ZF = 0)

Dec

DI

; если символ найден, то уменьшим DI, чтобы он

 

 

указывал на адрес найденного байта (символа ‘! ’)

Пример загрузки в аккумулятор слова из NAME1

CLD

 

 

LEA

SI, NAME1

; Загрузить первое слово

LODSW

; из NAME1 в AX

Пример пересылки в строку NAME3 из аккумулятора

LEA

DI, NAME3

 

MOV

CX, 05

 

MOV

AX, 2020h

; Переслать пробелы

REP STOSW

; в NAME3

8. ПЕРЕХОДЫ

МП выполняет команды программы друг за другом в том порядке, как они были описаны в исходном тексте программы и содержатся в выполнимом модуле. Если программисту требуется нарушить этот порядок, заставив МП обойти некоторый участок программы, перейти на выполнение другой ветви или передать управление подпрограмме, имея в виду после ее завершения вернуться на прежнее место, то используются команды переходов. Переходы разделяются:

на безусловные, когда передача управления в другую точку программы осуществляется в безусловном порядке, независимо ни от каких обстоятельств;

условные, осуществляемые или не осуществляемые в зависимости от тех или иных условий, результатов сравнения, анализа, поиска

ит. п.

34

Безусловные переходы осуществляются с помощью команды jmp, которая может использоваться в пяти разновидностях. Переход может быть:

прямым коротким (в пределах -128... + 127 байтов);

прямым ближним (в пределах текущего сегмента команд);

прямым дальним (в другой сегмент команд);

косвенным ближним (в пределах текущего сегмента команд че-

рез ячейку с адресом перехода);

косвенным дальним (в другой сегмент команд через ячейку с адресом перехода).

8.1. ПРЯМОЙ КОРОТКИЙ (SHORT) ПЕРЕХОД

Прямым называется переход, в команде которого в явной форме указывается метка, на которую нужно перейти. Разумеется, эта метка должна присутствовать в том же программном сегменте, при этом помеченная ею команда может находиться как до, так и после команды jmp. Достоинство команды короткого перехода заключается в том, что она занимает лишь два байта памяти: в первом байте записывается код операции (EBh), во втором – смещение к точке перехода. Расстояние до точки перехода отсчитывается от очередной команды, т. е. команды, следующей за командой jmp. Поскольку требуется обеспечить переход как вперед, так и назад, смещение рассматривается как число со знаком, и, следовательно, переход может быть осуществлен максимум на 127 байтов вперед или 128 байтов назад. Прямой короткий переход оформляется следующим образом:

code segment

 

 

jmp short go

; Код ЕВ dd

go:

code ends

При выполнении команды прямого короткого перехода процессор прибавляет значение байта dd к младшему байту текущего значения указателя команд IP (который, как уже говорилось, всегда указывает на команду, следующую за выполняемой). В результате в IP оказывается адрес точки перехода, а предложения, находящиеся между командой

35

jmp и точкой перехода, не выполняются. Конструкция с прямым переходом вперед часто используется для того, чтобы обойти данные, которые по каким-то причинам желательно разместить в сегменте команд.

8.2. ПРЯМОЙ БЛИЖНИЙ (NEAR), ИЛИ ВНУТРИСЕГМЕНТНЫЙ ПЕРЕХОД

Этот переход отличается от предыдущего только тем, что под сме-

щение к точке перехода отводится целое слово. Это дает возможность осуществить переход в любую точку 64-килобайтного сегмента.

code segment

jmp go ;Код Е9 dddd

go:

code ends

Метка go может находиться в любом месте сегмента команд как до, так и после команды jmp. В коде команды dddd обозначает слово с величиной относительного смещения к точке перехода от команды, следующей за командой jmp.

При выполнении команды прямого ближнего перехода процессор должен прибавить значение слова dddd к текущему значению указателя команд IP и сформировать тем самым адрес точки перехода.

Итак, с помощью команды ближнего перехода (команда jmp без ка- ких-либо спецификаторов) можно перейти в любую точку в пределах данного сегмента команд. Для того чтобы перейти в другой сегмент

команд, следует воспользоваться командой дальнего перехода.

8.3. ПРЯМОЙ ДАЛЬНИЙ (FAR), ИЛИ МЕЖСЕГМЕНТНЫЙ ПЕРЕХОД

Этот переход позволяет передать управление в любую точку любого сегмента (предполагается, что программный комплекс включает несколько сегментов команд). Команда дальнего перехода имеет длину пять байтов и включает, кроме кода операции EAh, еще и полный адрес

36

точки перехода, т. е. сегментный адрес и смещение. Транслятору надо сообщить, что этот переход – дальний (по умолчанию команда jmp транслируется, как команда ближнего перехода). Это делается с помощью описателя far ptr, указываемого перед именем точки перехода.

code1 segment

assume CS: codel ; Сообщим транслятору, что это сегмент команд

jmp far ptr go ;Код EA dddd ssss

codel ends code2 segment

assume CS : code2 ; Сообщим транслятору, что это сегмент команд

gо:

code2 ends

Метка go находится в другом сегменте команд этой двухсегментной программы. В коде команды ssss – сегментный адрес сегмента code2, a dddd – смещение точки перехода go в сегменте команд code2.

При наличии в программе нескольких сегментов команд каждый из них необходимо предварять директивой Ассемблера assume СS:имя_сегмента, которая сообщает транслятору о том, что начался очередной сегмент команд. Это поможет транслятору правильно обрабатывать адреса меток, встречающихся в этом сегменте.

8.4. КОСВЕННЫЙ БЛИЖНИЙ (ВНУТРИСЕГМЕНТНЫЙ) ПЕРЕХОД

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

ственно иметь много разных вариантов. В них адрес перехода не указывается явным образом в виде метки, а содержится либо в ячейке памяти, либо в одном из регистров . Это позволяет при необходимости модифицировать адрес перехода, а также осуществлять переход по известному абсолютному адресу. Рассмотрим случай, когда адрес перехода хранится в ячейке сегмента данных. Если переход ближний, то

37

ячейка с адресом состоит из одного слова и содержит только смещение к точке перехода.

code segment

jmp DS:go_addr ;Код FF 26 dddd

go: ; Точка перехода

code ends

data segment

go_addr dw go ;Адрес перехода (слово)

data ends

Точка перехода go может находиться в любом месте сегмента команд. В коде команды dddd обозначает относительный адрес слова go_addr в сегменте данных, содержащем эту ячейку.

Достоинства косвенного перехода: ячейка go_addr вначале может быть пуста, а по ходу выполнения программы в нее, в зависимости от каких-либо условий, помещается адрес той или иной точки перехода:

mov go_addr, offset gol mov go_addr, offset go2 mov go_addr, offset go3

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

Ассемблер допускает различные формы описания косвенного перехода через ячейку сегмента данных:

jmp DS:go_addr jmp word ptr go_addr jmp go_addr

В первом варианте, использованном в приведенном выше фрагменте, указано, через какой сегментный регистр надлежит обращаться к ячейке go_addr, содержащей адрес перехода. Здесь допустима замена

38

сегмента, если сегмент с ячейкой go_addr адресуется через другой сегментный регистр, например, ES или CS.

Во втором варианте подчеркивается, что переход осуществляется через ячейку размером в одно слово и, следовательно, является ближним. Ячейка go_addr могла быть объявлена с помощью директивы dd и содержать полный двухсловный адрес перехода, требуемый для реализации дальнего перехода. Однако ею можно воспользоваться и для ближнего перехода. Описатель word ptr перед именем ячейки с адресом перехода заставит транслятор считать, что она имеет размер одно слово (независимо от того, как она была объявлена), и что переход, следовательно, является ближним.

Наконец, возможен и самый простой, третий вариант, который совпадает по форме с прямым переходом, но тем не менее является косвенным, так как символическое обозначение go_addr представляет собой имя поля данных, а не программную метку. В этом варианте предполагается, что сегмент, в котором находится ячейка go_addr, адресуется по умолчанию через регистр DS, хотя, как и во всех таких случаях, допустима замена сегмента.

В приведенных примерах адрес поля памяти с адресом точки перехода задавался непосредственно в коде команды косвенного перехода. Однако этот адрес можно задать и в одном из регистров общего назначения (ВХ, SI или DI). Для приведенного выше примера косвенного перехода в точку go, адрес которой находится в ячейке go_addr в сегменте данных, переход с использованием косвенной регистровой адресации может выглядеть следующим образом:

mov BX, offset go_addr

;в ВХ смещение поля с адресом перехода

jmp [BX]

;Переход в точку gо

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

39

code segment

 

 

mov BX, off set go_tbl

;Загружаем в ВХ базовый адрес таблицы

mov SI, 4

;Вычисленное каким-то образом смещение в таблице

jmp [BX] [SI]

 

;Если индекс = 4, переход в точку goЗ

 

 

gol:

 

;1-я точка перехода

 

 

gо2 :

 

;2-я точка перехода

 

 

gоЗ:

 

;3-я точка перехода

 

 

code ends

 

 

data segment

go_tbl label word ;Таблица адресов переходов

gol_addr dw gol ;Адрес первой альтернативной точки перехода go2_addr dw go2 ;Адрес второй альтернативной точки перехода go3_addr dw доЗ ;Адрес третьей альтернативной точки перехода data ends

Приведенный пример носит условный характер. В реальной программе индекс, помещаемый в регистр SI, должен вычисляться по результатам анализа некоторых условий.

8.5. КОСВЕННЫЙ ДАЛЬНИЙ (МЕЖСЕГМЕНТНЫЙ) ПЕРЕХОД

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

codel segment

assume CS:codel,DS:data

jmp DS:go_addr ; Код FF 2E dddd

codel ends

40

code2 segment assume CS:code2

go: ;Точка перехода в другом сегменте команд

code2 ends data segment

go_addrdd go ;Двухсловный адрес точки перехода

data ends

Точка перехода go находится в другом сегменте команд этой двухсегментной программы. В коде команды dddd обозначает относительный адрес слова go_addr в сегменте данных. Ячейка go_addr объявляется директивой dd (define double, определить двойное слово) и содержит двухсловный адрес точки перехода: в первом слове содержится смещение go в сегменте команд codel, во втором слове сегментный адрес codel. Оба компонента адреса перехода могут быть вычислены и помещены в ячейку go_addr по ходу выполнения программы.

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

jmp DS:go_addr ;Возможна замена сегмента

jmp dword ptr go_addr ;Если поле go_addr объявлено операторами dw jmp go_addr ;Характеристики ячейки должны быть известны

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

mov BX, offset go_addr

jmp [BX]

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

41

8.6. УСЛОВНЫЙ ПЕРЕХОД

Ассемблер поддерживает большое количество команд условного перехода, которые осуществляют передачу управления в зависимости от состояний флагового регистра. Например, при сравнении содержимого двух полей последующий переход зависит от значения флага.

Команду LOOP , которая рассматривалась выше, можно заменить на две команды:

одна уменьшает содержимое регистра CX;

другая выполняет условный переход.

Команда LOOP A20 эквивалентна двум командам

DEC CX

JNZ A20

Команды DEC и JNZ действуют аналогично команде LOOP: уменьшают содержимое регистра CX на 1 и выполняют переход на метку A20, если в CX не ноль. Команда DEC, кроме того, устанавливает флаг нуля во флаговом регистре в состояние 0 или 1. Команда JNZ затем проверяет эту установку.

Операнд в команде JNZ cодержит значение расстояния между концом команды JNZ и адресом A20, которое прибавляется к командному указателю. Это расстояние должно быть в пределах от –128 до + 127 байт. В случае перехода за эти границы Ассемблер выдаст сообщение «Relative jump out of range» (превышены относительные границы перехода).

Знаковые и беззнаковые данные

Рассматривая назначение команд условного перехода, следует пояснить характер их использования. Типы данных, над которыми выполняются арифметические операции и операции сравнения, определяют, какими командами пользоваться: беззнаковыми или знаковыми. Беззнаковые данные используют все биты как биты данных; характерным примером являются символьные строки: имена, адреса и натуральные числа. В знаковых данных самый левый бит представляет собой знак, причем если его значение равно нулю, то число положительное, если единице, то отрицательное.

В качестве примера предположим, что регистр AX содержит

11000110, а BX – 00010110. Команда CMP AX, BX сравнивает содер-

жимое регистров AX и BX. Если данные беззнаковые, то значение в

42

AX больше, а если знаковые, то меньше (тогда в аккумуляторе отрицательное число).

Переходы для беззнаковых данных представлены в табл. 1.

 

 

Т а б л и ц а 1

 

 

 

Мнемоника

Описание

Проверяемые флаги

JE/JZ

Переход, если равно/нуль

ZF

JNE/JNZ

Переход, если не равно/не нуль

ZF

JA/JNBE

Переход, если выше/не ниже или равно

ZF,CF

JAE/JNB

Переход, если выше или равно/не ниже

CF

JB/JNAE

Переход, если ниже/не выше или равно

CF

JBE/JNA

Переход, если ниже или равно/не выше

CF,AF

Любую проверку можно кодировать одним из двух мнемонических кодов. Например, JB и JNAE генерирует один и тот же объектный код, хотя положительную проверку JB легче понять, чем отрицательную

JNAE.

Переходы для знаковых данных представлены в табл. 2.

 

 

Т а б л и ц а 2

 

 

 

Мнемоника

Описание

Проверяемые флаги

JE/JZ

Переход, если равно/нуль

ZF

JNE/JNZ

Переход, если не равно/не нуль

ZF

JG/JNLE

Переход, если больше/не меньше

ZF,SF,OF

 

или равно

 

JGE/JNL

Переход, если больше или равно/

SF,OF

 

не меньше

 

JL/JNGE

Переход, если меньше/не больше

SF,OF

 

или равно

 

JLE/JNG

Переход, если меньше или равно/

ZF,SF,OF

 

не больше

 

Команды перехода для условия равно или ноль (JE/JZ) и не равно или не ноль (JNE/JNZ) присутствуют в обоих списках для беззнаковых и знаковых данных. Состояние равно/ноль происходит вне зависимости от наличия знака.

43

Специальные арифметические проверки представлены в табл. 3.

 

 

Т а б л и ц а 3

 

 

 

Мнемоника

Описание

Проверяемые флаги

JS

Переход, если есть знак (отрицательно)

SF

JNS

Переход, если нет знака (положительно)

SF

JC

Переход, если есть перенос (как JB)

CF

JNC

Переход, если нет переноса

CF

JO

Переход, если есть переполнение

OF

JNO

Переход, если нет переполнения

OF

JP/JPE

Переход, если паритет четный

PF

JNP/JP

Переход, если паритет нечетный

PF

Итак, для беззнаковых данных есть переходы по состояниям равно,

выше или ниже, а для знаковых – равно, больше или меньше. Ко-

манды JAE и JGE, являясь явно одинаковыми, проверяют различные флаги.

9.ВЫЗОВЫ ПРОЦЕДУР

Впредыдущих разделах примеры содержали в кодовом сегменте только oдну процедуру, оформленную следующим образом:

BEGIN PROC ….

BEGIN ENDP

Кодовый сегмент, однако, может содержать любое количество процедур, которые разделяются директивами PROC и ENDP. Типичная организация многопроцедурной программы приведена ниже:

CODESG SEGMENT PARA

BEGIN PROC FAR

……………………….

CALL B10

CALL C10 RET

44

BEGIN ENDP B10 PROC NEAR

……………………

RET

B10 ENDP

C10 PROC NEAR

………………….

RET

C10 ENDP CODESG ENDS END BEGIN

Обратите внимание на следующие особенности.

Директивы PROC по меткам B10 и C10 имеют операнд NEAR для указания того, что эти процедуры находятся в текущем кодовом сегменте. Во многих последующих примерах этот операнд опущен, так как по умолчанию Ассемблер принимает тип NEAR.

Каждая процедура имеет уникальное имя и содержит собствен-

ную директиву ENDP для указания конца процедуры.

Для передачи управления в процедуре BEGIN имеются две команды:

CALL B10 и CALL C10. В результате первой команды CALL управление передается процедуре B10 и начинается ее выполнение. Достигнув команды RET, управление возвращается на команду непосредственно следующую за CALL B10. Вторая команда CALL действует аналогично – передает управление в процедуру C10, выполняет

еекоманды и возвращает управление по команде RET.

Команда RET всегда выполняет возврат в вызывающую программу.

Программа BEGIN вызывает процедуры B10 и C10, которые возвращают управление обратно в BEGIN. Для выполнения самой программы BEGIN операционная система DOS вызывает ее и в конце выполнения команда RET возвращает управление в DOS. Если процедура B10 не содержит завершающей команды RET, то выполнение команд

продолжится из B10 непосредственно в процедуре C10. Если процедура C10 не содержит команды RET, то будут выполняться команды, оказавшиеся за процедурой C10 с непредсказуемым результатом.

Использование процедур дает хорошую возможность организовать логическую структуру программы. Кроме того, операнды для

45

команды CALL могут иметь значения, выходящие за границу от –128 до + 127 байт.

Технически управление в процедуру типа NEAR может быть передано с помощью команд перехода или даже обычным построчным кодированием. Но в большинстве случаев рекомендуется использовать команду CALL для передачи управления в процедуру и команду RET для возврата.

Итак, CALL – команда вызова процедуры, передает управление процедуре, сохранив в стеке адрес возврата. Команда RET, которой оканчивается процедура, запрашивает из стека адрес возврата и возвращает управление команде, следующей за командой вызова.

Вызовы (CALL) можно разделить на четыре разновидности:

1)прямой ближний, в пределах текущего сегмента команд;

2)прямой дальний, в другом сегменте команд;

3)косвенный ближний, в пределах текущей сегмента команд через ячейку с адресом перехода;

4)косвенный дальний, в другом сегменте команд через ячейку с адресом перехода.

9.1. ПРЯМОЙ БЛИЖНИЙ ВЫЗОВ

seg

segment ‘code’

mymain proc

…………………

call sub

…………………

mymain endp

sub

proc near

 

…………………

 

ret

sub

endp

seg

ends

При выполнении команды CALL микропроцессор помещает адрес возврата (содержимое IP) в стек, после чего к текущему значению IP прибавляет смещение до sub. В IP теперь начальный адрес процедуры sub. Команда ret процедуры помещает адрес возврата из стека в IP.

46

9.2. КОСВЕННЫЙ БЛИЖНИЙ ВЫЗОВ

Для косвенного вызова адрес перехода извлекается не из кода команды, а из памяти или регистра.

seg1

segment ‘code’

mymain

proc

…………………

call word ptr subadr

mymain

endp

sub

proc near

…………………

ret

 

sub

endp

seg

ends

dat

segment

…………………

subadr dw sub

…………………

dat ends

Возможны следующие варианты вызовов: call word ptr[bx] –если в bx адрес процедуры;

call word ptr[bx][si] –если в bx адрес таблицы адресов процедур,

а в si – номер конкретной процедуры;

call word ptr table[si] – если table –адрес таблицы адресов, а в si – номер конкретной процедуры.

9.3. ПРЯМОЙ ДАЛЬНИЙ ВЫЗОВ

Для прямого дальнего вызова в стек заносится два слова: сегментный адрес возврата и относительный адрес (из IP). Оба значения берутся из самой команды вызова.

seg1 segment `code` mymain proc

call far ptr sub

mymain endp seg1 ends

47

seg2

segment `code`

sub

proc far

 

 

ret

sub

endp

seg2

ends

 

9.4. КОСВЕННЫЙ ДАЛЬНИЙ ВЫЗОВ

seg1 segment `code`

mymain

proc

 

call dword ptr subadr

 

mymain

endp

seg1

ends

 

seg2

segment `code`

sub

proc far

 

 

ret

sub

endp

seg2

ends

 

dat

segment

subadr

dd sub

…………………

dat

ends

10. ОСНОВНЫЕ КОМАНДЫ ПЕРЕСЫЛКИ, ОБМЕНА, ВВОДА-ВЫВОДА

MOV – пересылка данных.

В результате выполнения первый операнд замещается вторым.

mov ax,bx

Запрещено:

– осуществлять пересылку из памяти в память (используется команда MOVS);

48

загружать сегментный регистр непосредственным значением (только через регистр);

пересылать из сегментного регистра в сегментный регистр (можно через стек)

mov ax,ds mov es, ax

или push ds

pop es

XCHG – обмен данными между операндами mov ax, ff01h

mov si, 1000h xchg ax,si

Врезультате выполнения команды xchg ax,si

вАХ будет 1000h, а в si – ff01h.

IN ввод из порта.

Вводит в AL или в AX из порта (внешнего устройства), адрес которого в DX. Если адрес порта меньше 255, тогда можно указывать непосредственное значение.

in al, 60h

; ввод в al из 60-го порта

mov dx, 3d5h

 

in ax, dx

ввод из порта с адресом 3d5h.

OUT – вывод в порт.

Адрес порта должен находиться в DX, если адрес больше 255, иначе можно указывать непосредственное значение.

out 61h,AL ; то что находится в AL, пишем по адресу 61, т. е. по адресу конкретного внешнего устройства – адресу порта.

LDS – загрузка указателя с использованием сегментного регистра DS, при этом загружается полный адрес: смещение в указанный в команде регистр, сегментный адрес в DS.

string db …………..

addr dd string

lds si,addr ; после выполнения команды DS:SI указывают на строку.

LES загрузка указателя с использованием системного регистра

ES; считывается двойное слово из памяти и размещаются в ES и указанном регистре. Для предыдущего примера команда les si,addr позво-

ляет создать указатель на строку через пару регистров ES:SI.

49

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