Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скляров И. Изучаем Assembler за 7 дней (2010).pdf
Скачиваний:
1335
Добавлен:
23.02.2015
Размер:
2.11 Mб
Скачать

http://www.sklyaroff.ru

55

Типы far и near в программе должны обязательно указываться с описателем PTR. Примеры:

jmp Lab

; ближний переход (near) (3 байта)

jmp short Lab

; короткий переход (2 байта)

jmp near ptr Lab

; ближний переход (3 байта)

jmp far ptr Lab

; дальний переход (5 байт)

jmp short $+2

; это команда просто передает

;управление на следующую команду.

3.3.Сравнение и условные переходы

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

Jcc метка

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

e — равно (equal), n — не (not),

g — больше (greater), l — меньше (less),

a — выше (above), b — ниже (below).

Условием для команд условного перехода служат отдельные флаги в регистре флагов EFLAGS/FLAGS. Например, команда JZ проверяет флаг нуля, поэтому если в какойлибо программе встретится команда JZ и при этом в регистре флагов будет установлен в 1 флаг нуля (ZF=1), то произойдет переход на указанную метку, если же флаг нуля будет установлен в нуль (ZF=0), то переход не осуществится. В табл. 3.1 перечислены все команды условных переходов и флаги, значения которых они проверяют.

Таблица 3.1. Команды условных переходов

Команда

Условие для CMP

Значения флагов

 

 

 

ja

Если выше

CF=0 и ZF=0

jnbe

Если не ниже и не равно

 

 

 

 

jae

Если выше или равно

CF=0

jnb

Если не ниже

 

jnc

Если перенос

 

 

 

CF=1

jb

Если ниже

jnae

Если не выше и не равно

 

jc

Если перенос

 

 

 

 

jbe

Если ниже или равно

CF=1 и ZF=1

jna

Если не выше

 

 

 

 

jcxz

Если CX=0

Не влияет на флаги

jecxz

Если ECX=0

 

 

 

 

je

Если равно

ZF=1

jz

Если ноль

 

 

 

 

 

http://www.sklyaroff.ru

 

 

56

 

 

 

 

Таблица 3.1. (окончание)

 

 

 

 

 

 

jg

 

Если больше

ZF=0 и SF=OF

 

jnle

 

Если не меньше и не равно

 

 

 

 

 

 

 

 

jge

 

Если больше или равно

SF=OF

 

jnl

 

Если не меньше

 

 

 

 

 

 

 

 

jl

 

Если меньше

SF<>OF

 

jnge

 

Если не больше и не равно

 

 

 

 

 

 

 

 

jle

 

Если меньше или равно

ZF=1 или SF<>OF

 

jng

 

Если не больше

 

 

 

 

 

 

 

 

jne

 

Если не равно

ZF=0

 

jnz

 

Если не ноль

 

 

 

 

 

 

 

 

jno

 

Если нет переполнения

OF=0

 

 

 

 

 

 

jnp

 

Если нет четности

PF=0

 

jpo

 

Если нечетное

 

 

 

 

 

 

 

 

jns

 

Если нет знака

SF=0

 

 

 

 

 

 

jp

 

Если есть четность

PF=1

 

jpe

 

Если четное

 

 

 

 

 

 

 

 

jo

 

Если есть переполнение

OF=1

 

 

 

 

 

 

js

 

Если есть знак

SF=1

 

 

 

 

 

 

Слова "выше" и "ниже" в таблице употребляются при сравнении беззнаковых целых чисел, слова "больше" и "меньше" учитывают знак. Например: число -1 меньше нуля, но если его рассматривать как беззнаковое, то оно будет выше нуля, потому что примет значение 65535 (0FFFFh). При выборе команды условного перехода в своей программе вы должны учитывать, будет выполняться сравнение знаковых или беззнаковых чисел.

Если команды условного перехода только проверяют флаги, то понятно, что должны присутствовать в ассемблере команды меняющие флаги. Обычно флаги меняют арифметические команды, устанавливая, например, флаг переноса или переполнения в результате своей работы (с арифметическими командами мы познакомимся на пятом дне). Эти флаги могут проверять соответствующие команды условного перехода, выполняя переход на метку или не выполняя. Однако чаще всего команды условного перехода работают совместно с командой сравнения (CMP происходит от англ. compare — сравнение):

CMP op1,op2

Эта команда сравнивает величины op1 и op2 и устанавливает соответствующие флаги в регистре флагов. На самом деле эта команда просто вычисляет разность op1-op2 без сохранения результата. Понятно, что возможны следующие варианты: op1<op2, op1<=op2, op1=op2, op1>=op2 и op1>op2. Таблица 3.1 поможет вам определить условия CMP, при которых срабатывают соответствующие переходы. Например, если операнды op1 и op2 не равны между собой, то команда JNE выполнит переход на указанную метку.

В листинге 3.4 показана программа, которая сравнивает два числа, задаваемые в регистрах AX и BX, и выводит результат сравнения на экран. Ассемблирование выполняется как обычно:

ml file.asm

После выполнения программа выдаст на экран результат:

http://www.sklyaroff.ru

57

Число в AX больше, чем в BX.

Сравнение осуществляется с учетом знака, поэтому можно в AX и BX задавать как положительные, так и отрицательные числа. Попробуйте подставлять различные значения и после ассемблирования посмотреть результаты.

Листинг 3.4. Сравнение двух чисел (jump2.asm)

.model tiny

.code

org 100h

start:

 

mov

ax,3

mov

bx,-15

cmp

ax,bx

jle

Lab

mov

ah,9

mov

dx,offset message1

int

21h

ret

 

Lab:

 

mov

ah,9

mov

dx,offset message2

int

21h

ret

 

message1

db "Число в AX больше, чем в BX.",0Dh,0Ah,'$'

message2

db "Число в AX меньше или равно значению в BX.",0Dh,0Ah,'$'

end

start

Команды CMP и Jcc вместе аналогичны оператору if... then..., который имеется практически во всех языках высокого уровня. Например, команда на языке BASIC:

if AX=>BX then goto Lab

на языке ассемблера будет выглядеть следующим образом: cmp ax,bx

jge Lab

3.4. Стек

Основными двумя командами для работы со стеком являются: PUSH (от англ. — вталкивать) и POP (от англ. — выталкивать), имеющие следующий синтаксис:

PUSH источник

POP приемник

Команда PUSH записывает в стек свой операнд, а команда POP считывает слово с вершины стека и присваивает его указанному операнду.

http://www.sklyaroff.ru

58

Например, после выполнения следующих команд

mov ax,777

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

push ax

; записать содержимое AX в стек

pop bx

; извлечь из стека

в регистре BX окажется значение 777.

Кроме основных двух команд существуют команды для записи в стек и чтения из стека регистра флагов:

Для 16-битного режима:

PUSHF — записывает регистр флагов FLAGS в стек.

POPF — считывает регистр флагов FLAGS из стека. Для 32-битного режима:

PUSHFD — записывает регистр флагов EFLAGS в стек.

POPFD — считывает регистр флагов EFLAGS из стека.

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

Понятно, что команды PUSHF и PUSHFD не меняют флаги, а команды POPF и POPFD, естественно, меняют все флаги.

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

Для 16-битного режима:

PUSHA — размещает в стеке регистры в следующем порядке: AX, CX, DX, BX, SP, BP, SI, DI (для SP берется значение, которое находилось в этом регистре до начала работы команды).

POPA — выполняет действие обратное PUSHA, но помещенное в стек значение SP игнорируется.

Для 32-битного режима:

PUSHAD — размещает в стеке регистры в следующем порядке: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI (для ESP берется значение, которое находилось в регистре до начала работы команды).

POPAD — выполняет действие обратное PUSHAD, но помещенное в стек значение ESP игнорируется.

Кроме того, процессоры 8086 и 8088 не позволяли в команде PUSH использовать непосредственный операнд, такая возможность впервые появилась в процессоре

80186.

Например:

push 7 ; такая команда допустима только в процессорах 80186 и выше

В процессорах 8086 и 8088 нужно использовать регистр: mov ax,7

push ax

Так как по умолчанию ассемблер разрешает использовать только команды процессора 8086, то для того, чтобы можно было использовать команды процессора 80186, надо перед первой из таких команд в программе задать директиву .186 или выше (.286, .386, .586…):

.186

 

pusha

; без директивы .186 ассемблер будет считать

 

; эту команду ошибочной

push 100h

; без директивы .186 ассемблер будет считать

 

; эту команду ошибочной

pushf

; эту команду можно использовать без директивы .186