5101

Системное программирование. Конспект лекций

Конспект

Информатика, кибернетика и программирование

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

Русский

2012-12-03

1.79 MB

179 чел.

Конспект лекций Системное программирование

Лекция1. Об ассемблере (1 пара)

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

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

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

По-настоящему решить проблемы, связанные с аппаратурой (или даже, более того, зависящие от аппаратуры как, к примеру, повышение быстродействия программы), невозможно без знания ассемблера.

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

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

Типичный современный компьютер (на базе i486 или Pentium) состоит из следующих компонентов (рис. 1).

Рис. 1. Компьютер и периферийные устройства

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

Рис. 2. Структурная схема персонального компьютера 

Обсудим схему на рис. 2 в несколько нетрадиционном стиле. Человеку свойственно, встречаясь с чем-то новым, искать какие-то ассоциации, которые могут помочь ему познать неизвестное. Какие ассоциации вызывает компьютер? У меня, к примеру, компьютер часто ассоциируется с самим человеком. Почему?

У компьютера есть органы восприятия информации из внешнего мира — это клавиатура, мышь, накопители на магнитных дисках. На рис. 2 эти органы расположены справа от системных шин.  У компьютера есть органы “переваривающие” полученную информацию — это центральный процессор и оперативная память.  И, наконец, у компьютера есть органы речи, выдающие результаты переработки. Это также некоторые из устройств справа.

Современным компьютерам, конечно, далеко до человека. Их можно сравнить с существами, взаимодействующими с внешним миром на уровне большого, но ограниченного набора безусловных рефлексов.  Этот набор рефлексов образует систему машинных команд. На каком бы высоком уровне вы не общались с компьютером, в конечном итоге все сводится к скучной и однообразной последовательности машинных команд.  Каждая машинная команда является своего рода раздражителем для возбуждения того или иного безусловного рефлекса. Реакция на этот раздражитель всегда однозначная и “зашита” в блоке микрокоманд в виде микропрограммы. Эта микропрограмма и реализует действия по реализации машинной команды, но уже на уровне сигналов, подаваемых на те или иные логические схемы компьютера, тем самым управляя различными подсистемами компьютера. В этом состоит так называемый принцип микропрограммного управления.

Продолжая аналогию с человеком, отметим: для того, чтобы компьютер правильно питался, придумано множество операционных систем, компиляторов сотен языков программирования и т. д. Но все они являются, по сути, лишь блюдом, на котором по определенным правилам доставляется пища (программы) желудку (компьютеру). Только (вот досада!) желудок компьютера любит диетическую, однообразную пищу — подавай ему информацию структурированную, в виде строго организованных последовательностей нулей и единиц, комбинации которых и составляют машинный язык.

Таким образом, внешне являясь полиглотом, компьютер понимает только один язык — язык машинных команд. Конечно, для общения и работы с компьютером, необязательно знать этот язык, но практически любой профессиональный программист рано или поздно сталкивается с необходимостью его изучения. К счастью, программисту не нужно пытаться постичь значение различных комбинаций двоичных чисел, так как еще в 50-е годы программисты стали использовать для программирования символический аналог машинного языка, который назвали языком ассемблера. Этот язык точно отражает все особенности машинного языка. Именно поэтому, в отличие от языков высокого уровня, язык ассемблера для каждого типа компьютера свой.

Из всего вышесказанного можно сделать вывод, что, так как язык ассемблера для компьютера “родной”, то и самая эффективная программа может быть написана только на нем (при условии, что ее пишет квалифицированный программист). Здесь есть одно маленькое “но”: это очень трудоемкий, требующий большого внимания и практического опыта процесс. Поэтому реально на ассемблере пишут в основном программы, которые должны обеспечить эффективную работу с аппаратной частью. Иногда на ассемблере пишутся критичные по времени выполнения или расходованию памяти участки программы. Впоследствии они оформляются в виде подпрограмм и совмещаются с кодом на языке высокого уровня.

Лекция 2. Программная модель микропроцессора (1 пара)

На современном компьютерном рынке наблюдается большое разнообразие различных типов компьютеров. Поэтому возможно предположить возникновение у потребителя вопроса — как оценить возможности конкретного типа (или модели) компьютера и его отличительные особенности от компьютеров других типов (моделей). Рассмотрения для этого одной лишь только структурной схемы компьютера недостаточно, так как она принципиально мало чем различается у разных машин: у всех компьютеров есть оперативная память, процессор, внешние устройства.  Различными являются способы, средства и используемые ресурсы, с помощью которых компьютер функционирует как единый механизм. Чтобы собрать воедино все понятия, характеризующие компьютер с точки зрения его функциональных программно-управляемых свойств, существует специальный термин — архитектура ЭВМ.  Впервые понятие архитектура ЭВМ стало упоминаться с появлением машин 3-го поколения для их сравнительной оценки.

К изучению языка ассемблера любого компьютера имеет смысл приступать только после выяснения того, какая часть компьютера оставлена видимой и доступной для программирования на этом языке. Это так называемая программная модель компьютера, частью которой является программная модель микропроцессора, которая содержит 32 регистра в той или иной мере доступных для использования программистом.  Данные регистры можно разделить на две большие группы:

  •  16 пользовательских регистров;
  •  16 системных регистров.

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

Пользовательские регистры

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

  •  восемь 32-битных регистров, которые могут использоваться программистами для хранения данных и адресов (их еще называют регистрами общего назначения (РОН)):
    •  eax/ax/ah/al;
    •  ebx/bx/bh/bl;
    •  edx/dx/dh/dl;
    •  ecx/cx/ch/cl;
    •  ebp/bp;
    •  esi/si;
    •  edi/di;
    •  esp/sp.
  •  шесть регистров сегментов: cs, ds, ss, es, fs, gs;
  •  регистры состояния и управления:
    •  регистр флагов eflags/flags;
    •  регистр указателя команды eip/ip.

Рис. 1. Пользовательские регистры микропроцессоров i486 и Pentium 

Почему многие из этих регистров приведены с наклонной разделительной чертой?  Нет, это не разные регистры — это части одного большого 32-разрядного регистра. Их можно использовать в программе как отдельные объекты.  Так сделано для обеспечения работоспособности программ, написанных для младших 16-разрядных моделей микропроцессоров фирмы Intel, начиная с i8086.  Микропроцессоры i486 и Pentium имеют в основном 32-разрядные регистры. Их количество, за исключением сегментных регистров, такое же, как и у i8086, но размерность больше, что и отражено в их обозначениях — они имеют  приставку e (Extended).

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

Регистры общего назначения

Все регистры этой группы позволяют обращаться к своим  “младшим” частям (см. рис. 1).  Рассматривая этот рисунок, заметьте, что использовать для самостоятельной адресации можно только младшие 16 и 8-битные части этих регистров. Старшие 16 бит этих регистров как самостоятельные объекты недоступны. Это сделано, как мы отметили выше, для совместимости с младшими 16-разрядными моделями микропроцессоров фирмы Intel.

Перечислим регистры, относящиеся к группе регистров общего назначения. Так как эти регистры физически находятся в микропроцессоре внутри арифметико-логического устройства (АЛУ), то их еще называют регистрами АЛУ:

  •  eax/ax/ah/al (Accumulator register) — аккумулятор.  Применяется для хранения промежуточных данных. В некоторых командах использование этого регистра обязательно;
  •  ebx/bx/bh/bl (Base register) — базовый регистр.  Применяется для хранения базового адреса некоторого объекта в памяти;
  •  ecx/cx/ch/cl (Count register) — регистр-счетчик.  Применяется в командах, производящих некоторые повторяющиеся действия. Его использование зачастую неявно и скрыто в алгоритме работы соответствующей команды.  К примеру, команда организации цикла loop кроме передачи управления команде, находящейся по некоторому адресу, анализирует и уменьшает на единицу значение регистра ecx/cx;
  •  edx/dx/dh/dl (Data register) — регистр данных.  Так же, как и регистр eax/ax/ah/al, он хранит промежуточные данные. В некоторых командах его использование обязательно; для некоторых команд это происходит неявно.

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

  •  esi/si (Source Index register) — индекс источника.  Этот регистр в цепочечных операциях содержит текущий адрес элемента в цепочке-источнике;
  •  edi/di (Destination Index register) — индекс приемника (получателя).  Этот регистр в цепочечных операциях содержит текущий адрес в цепочке-приемнике.

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

  •  esp/sp (Stack Pointer register) — регистр указателя стека.  Содержит указатель вершины стека в текущем сегменте стека.
  •  ebp/bp (Base Pointer register) — регистр указателя базы кадра стека.  Предназначен для организации произвольного доступа к данным внутри стека.

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

Сегментные регистры

В программной модели микропроцессора имеется шесть сегментных регистров: cs, ss, ds, es, gs, fs.  Их существование обусловлено спецификой организации и использования оперативной памяти микропроцессорами Intel. Она заключается в том, что микропроцессор аппаратно поддерживает структурную организацию программы в виде трех частей, называемых сегментами. Соответственно, такая организация памяти называется сегментной.

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

  1.  Сегмент кода. Содержит команды программы.  Для доступа к этому сегменту служит регистр cs (code segment register) — сегментный регистр кода. Он содержит адрес сегмента с машинными командами, к которому имеет доступ микропроцессор (то есть эти команды загружаются в конвейер микропроцессора).
  2.  Сегмент данных. Содержит обрабатываемые программой данные.  Для доступа к этому сегменту служит регистр ds (data segment register) — сегментный регистр данных, который хранит адрес сегмента данных текущей программы.
  3.  Сегмент стека. Этот сегмент представляет собой область памяти, называемую стеком.  Работу со стеком микропроцессор организует по следующему принципу: последний записанный в эту область элемент выбирается первым. Для доступа к этому сегменту служит регистр ss (stack segment register) — сегментный регистр стека, содержащий адрес сегмента стека.
  4.  Дополнительный сегмент данных.  Неявно алгоритмы выполнения большинства машинных команд предполагают, что обрабатываемые ими данные расположены в сегменте данных, адрес которого находится в сегментном регистре ds.  Если программе недостаточно одного сегмента данных, то она имеет возможность использовать еще три дополнительных сегмента данных. Но в отличие от основного сегмента данных, адрес которого содержится в сегментном регистре ds, при использовании дополнительных сегментов данных их адреса требуется указывать явно с помощью специальных префиксов переопределения сегментов в команде.  Адреса дополнительных сегментов данных должны содержаться в регистрах es, gs, fs (extension data segment registers).

Регистры состояния и управления

В микропроцессор включены несколько регистров (см. рис. 1), которые постоянно содержат информацию о состоянии как самого микропроцессора, так и программы, команды которой в данный момент загружены на конвейер. К этим регистрам относятся:

  •  регистр флагов eflags/flags;
  •  регистр указателя команды eip/ip.

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

eflags/flags (flag register) — регистр флагов. Разрядность eflags/flags — 32/16 бит. Отдельные биты данного регистра имеют определенное функциональное назначение и называются флагами. Младшая часть этого регистра полностью аналогична регистру flags для i8086. На рис. 2 показано содержимое регистра eflags.

Рис. 2. Содержимое регистра eflags 

Исходя из особенностей использования, флаги регистра eflags/flags можно разделить на три группы:

  •  8 флагов состояния. Эти флаги могут изменяться после выполнения машинных команд.  Флаги состояния регистра eflags отражают особенности результата исполнения арифметических или логических операций. Это дает возможность анализировать состояние вычислительного процесса и реагировать на него с помощью команд условных переходов и вызовов подпрограмм. В табл. 1 приведены флаги состояния и указано их назначение;
  •  1 флаг управления. Обозначается df (Directory Flag).  Он находится в 10-м бите регистра eflags и используется цепочечными командами. Значение флага df определяет направление поэлементной обработки в этих операциях: от начала строки к концу (df = 0) либо наоборот, от конца строки к ее началу (df = 1).  Для работы с флагом df существуют специальные команды: cld (снять флаг df) и std (установить флаг df).  Применение этих команд позволяет привести флаг df в соответствие с алгоритмом и обеспечить автоматическое увеличение или уменьшение счетчиков при выполнении операций со строками;
  •  5 системных флагов, управляющих вводом/выводом, маскируемыми прерываниями, отладкой, переключением между задачами и виртуальным режимом 8086.  Прикладным программам не рекомендуется модифицировать без необходимости эти флаги, так как в большинстве случаев это приведет к прерыванию работы программы. В табл. 2 перечислены системные флаги, их назначение.

Таблица 1. Флаги состояния

Мнемоника флага

Флаг

Номер бита в eflags

Содержание и назначение

cf

Флаг переноса (Carry Flag)

0

1 — арифметическая операция произвела перенос из старшего бита результата. Старшим является 7, 15 или 31-й бит в зависимости от размерности операнда;  0 — переноса не было

pf

Флаг паритета (Parity Flag)

2

1 — 8 младших разрядов (этот флаг — только для 8 младших разрядов операнда любого размера) результата содержат четное число единиц;  0 — 8 младших разрядов результата содержат нечетное число единиц

af

Вспомогательный флаг переноса (Auxiliary carry Flag)

4

Только для команд работающих с BCD-числами. Фиксирует факт заема из младшей тетрады результата:  1 — в результате операции сложения был произведен перенос из разряда 3 в старший разряд или при вычитании был заем в разряд 3 младшей тетрады из значения в старшей тетраде;  0 — переносов и заемов в(из) 3 разряд(а) младшей тетрады результата не было

zf

Флаг нуля (Zero Flag)

6

1 — результат нулевой;  0 — результат ненулевой

sf

Флаг знака (Sign Flag)

7

Отражает состояние старшего бита результата (биты 7, 15 или 31 для 8, 16 или 32-разрядных операндов соответственно):  1 — старший бит результата равен 1;  0 — старший бит результата равен 0

of

Флаг переполнения (Overflow Flag)

11

Флаг of используется для фиксирования факта потери значащего бита при арифметических операциях:  1 — в результате операции происходит перенос (заем) в(из) старшего, знакового бита результата (биты 7, 15 или 31 для 8, 16 или 32-разрядных операндов соответственно);  0 — в результате операции не происходит переноса (заема) в(из) старшего, знакового бита результата

iopl

Уровень Привилегий ввода-вывода (Input/Output Privilege Level)

12, 13

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

nt

флажок вложенности задачи (Nested Task)

14

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

Таблица 2. Системные флаги

Мнемоника флага

Флаг

Номер бита в eflags

Содержание и назначение

tf

Флаг трассировки (Trace Flag)

8

Предназначен для организации пошаговой работы микропроцессора.  1 — микропроцессор генерирует прерывание с номером 1 после выполнения каждой машинной команды. Может использоваться при отладке программ, в частности отладчиками;  0 — обычная работа

if

Флаг прерывания (Interrupt enable Flag)

9

Предназначен для разрешения или запрещения (маскирования) аппаратных прерываний (прерываний по входу INTR).  1 — аппаратные прерывания разрешены;  0 — аппаратные прерывания запрещены

rf

Флаг возобновления (Resume Flag)

16

Используется при обработке прерываний от регистров отладки.

vm

Флаг виртуального (Virtual 8086 Mode)

17

Признак работы микропроцессора в режиме виртуального 8086.  1 — процессор работает в режиме виртуального 8086;  0 — процессор работает в реальном или защищенном режиме

ac

Флаг контроля выравнивания (Alignment Check)

18

Предназначен для разрешения контроля выравнивания при обращениях к памяти. Используется совместно с битом am в системном регистре cr0. К примеру, Pentium разрешает размещать команды и данные с любого адреса. Если требуется контролировать выравнивание данных и команд по адресам кратным 2 или 4, то установка данных битов приведет к тому, что все обращения по некратным адресам будут возбуждать исключительную ситуацию

eip/ip (Instraction Pointer register) — регистр-указатель команд.  Регистр eip/ip имеет разрядность 32/16 бит и содержит смещение следующей подлежащей выполнению команды относительно содержимого сегментного регистра cs в текущем сегменте команд. Этот регистр непосредственно недоступен программисту, но загрузка и изменение его значения производятся различными командами управления, к которым относятся команды условных и безусловных переходов, вызова процедур и возврата из процедур. Возникновение прерываний также приводит к модификации регистра eip/ip.

Системные регистры микропроцессора

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

Системные регистры можно разделить на три группы:

  •  четыре регистра управления;
  •  четыре регистра системных адресов;
  •  восемь регистров отладки.

Регистры управления

В группу регистров управления входят 4 регистра:  cr0, cr1, cr2, cr3.

Эти регистры предназначены для общего управления системой.  Регистры управления доступны только программам с уровнем привилегий 0.

Хотя микропроцессор имеет четыре регистра управления, доступными являются только три из них — исключается cr1, функции которого пока не определены (он зарезервирован для будущего использования).

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

  •  pe (Protect Enable), бит 0 — разрешение защищенного режима работы.  Состояние этого флага показывает, в каком из двух режимов — реальном (pe=0) или защищенном (pe=1) — работает микропроцессор в данный момент времени.
  •  mp (Math Present), бит 1 — наличие сопроцессора. Всегда 1.
  •  ts (Task Switched), бит 3 — переключение задач.  Процессор автоматически устанавливает этот бит при переключении на выполнение другой задачи.
  •  am (Aligment Mask), бит 18 — маска выравнивания.  Этот бит разрешает (am = 1) или запрещает (am = 0) контроль выравнивания.
  •  cd (Cache Disable), бит 30, — запрещение кэш-памяти.  С помощью этого бита можно запретить (cd = 1) или разрешить (cd = 0) использование внутренней кэш-памяти (кэш-памяти первого уровня).
  •  pg (PaGing), бит 31, — разрешение (pg = 1) или запрещение (pg = 0) страничного преобразования.  Флаг используется при страничной модели организации памяти.

Регистр cr2 используется при страничной организации оперативной памяти для регистрации ситуации, когда текущая команда обратилась по адресу, содержащемуся в странице памяти, отсутствующей в данный момент времени в памяти.  В такой ситуации в микропроцессоре возникает исключительная ситуация с номером 14, и линейный 32-битный адрес команды, вызвавшей это исключение, записывается в регистр cr2. Имея эту информацию, обработчик исключения 14 определяет нужную страницу, осуществляет ее подкачку в память и возобновляет нормальную работу программы;

Регистр cr3 также используется при страничной организации памяти.  Это так называемый регистр каталога страниц первого уровня. Он содержит 20-битный физический базовый адрес каталога страниц текущей задачи. Этот каталог содержит 1024 32-битных дескриптора, каждый из которых содержит адрес таблицы страниц второго уровня. В свою очередь каждая из таблиц страниц второго уровня содержит 1024 32-битных дескриптора, адресующих страничные кадры в памяти. Размер страничного кадра — 4 Кбайт.

Регистры системных адресов

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

При работе в защищенном режиме микропроцессора адресное пространство делится на:

  •  глобальное — общее для всех задач;
  •  локальное — отдельное для каждой задачи.

Этим разделением и объясняется присутствие в архитектуре микропроцессора следующих системных регистров:

  •  регистра таблицы глобальных дескрипторов gdtr (Global Descriptor Table Register) имеющего размер 48 бит и содержащего 32-битовый (биты 16—47) базовый адрес глобальной дескрипторной таблицы GDT и 16-битовое (биты 0—15) значение предела, представляющее собой размер в байтах таблицы GDT;
  •  регистра таблицы локальных дескрипторов ldtr (Local Descriptor Table Register) имеющего размер 16 бит и содержащего так называемый селектор дескриптора локальной дескрипторной таблицы LDT. Этот селектор является указателем в таблице GDT, который и описывает сегмент, содержащий локальную дескрипторную таблицу LDT;
  •  регистра таблицы дескрипторов прерываний idtr (Interrupt Descriptor Table Register) имеющего размер 48 бит и содержащего 32-битовый (биты 16–47) базовый адрес дескрипторной таблицы прерываний IDT и 16-битовое (биты 0—15) значение предела, представляющее собой размер в байтах таблицы IDT;
  •  16-битового регистра задачи tr (Task Register), который подобно регистру ldtr, содержит селектор, то есть указатель на дескриптор в таблице GDT. Этот дескриптор описывает текущий сегмент состояния задачи (TSS — Task Segment Status). Этот сегмент создается для каждой задачи в системе, имеет жестко регламентированную структуру и содержит контекст (текущее состояние) задачи. Основное назначение сегментов TSS — сохранять текущее состояние задачи в момент переключения на другую задачу.

Регистры отладки

Это очень интересная группа регистров, предназначенных для аппаратной отладки. Средства аппаратной отладки впервые появились в микропроцессоре i486. Аппаратно микропроцессор содержит восемь регистров отладки, но реально из них используются только 6.

Регистры dr0, dr1, dr2, dr3 имеют разрядность 32 бит и предназначены для задания линейных адресов четырех точек прерывания. Используемый при этом механизм следующий: любой формируемый текущей программой адрес сравнивается с адресами в регистрах dr0...dr3, и при совпадении генерируется исключение отладки с номером 1.

Регистр dr6 называется регистром состояния отладки. Биты этого регистра устанавливаются в соответствии с причинами, которые вызвали возникновение последнего исключения с номером 1.

Перечислим эти биты и их назначение:

  •  b0 — если этот бит установлен в 1, то последнее исключение (прерывание) возникло в результате достижения контрольной точки, определенной в регистре dr0;
  •  b1 — аналогично b0, но для контрольной точки в регистре dr1;
  •  b2 — аналогично b0, но для контрольной точки в регистре dr2;
  •  b3 — аналогично b0, но для контрольной точки в регистре dr3;
  •  bd (бит 13) — служит для защиты регистров отладки;
  •  bs (бит 14) — устанавливается в 1, если исключение 1 было вызвано состоянием флага tf = 1 в регистре eflags;
  •  bt (бит 15) устанавливается в 1, если исключение 1 было вызвано переключением на задачу с установленным битом ловушки в TSS t = 1.

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

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

  •  место регистрации контрольной точки — только в текущей задаче или в любой задаче. Эти биты занимают младшие восемь бит регистра dr7 (по два бита на каждую контрольную точку (фактически точку прерывания), задаваемую регистрами dr0, dr1, dr2, dr3 соответственно).  Первый бит из каждой пары — это так называемое локальное разрешение; его установка говорит о том, что точка прерывания действует если она находится в пределах адресного пространства текущей задачи.  Второй бит в каждой паре определяет глобальное разрешение, которое говорит о том, что данная контрольная точка действует в пределах адресных пространств всех задач, находящихся в системе;
  •  тип доступа, по которому инициируется прерывание: только при выборке команды, при записи или при записи/чтении данных. Биты, определяющие подобную природу возникновения прерывания, локализуются в старшей части данного регистра.

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


Лекция 3. Структура программы на ассемблере (2 пары)

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

Предложения ассемблера бывают четырех типов:

  •  команды или инструкции, представляющие собой символические аналоги машинных команд.
  •  В процессе трансляции инструкции ассемблера преобразуются в соответствующие команды системы команд микропроцессора;
  •  макрокоманды — оформляемые определенным образом предложения текста программы, замещаемые во время трансляции другими предложениями;
  •  директивы, являющиеся указанием транслятору ассемблера на выполнение некоторых действий. У директив нет аналогов в машинном представлении;
  •  строки комментариев, содержащие любые символы, в том числе и буквы русского алфавита. Комментарии игнорируются транслятором.

Синтаксис ассемблера

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

Рис. 1. Формат предложения ассемблера

Рис. 2. Формат директив

Рис. 3. Формат команд и макрокоманд

На этих рисунках:

  •  имя метки — идентификатор, значением которого является адрес первого байта того предложения исходного текста программы, которое он обозначает;
  •  имя — идентификатор, отличающий данную директиву от других одноименных директив. В результате обработки ассемблером определенной директивы этому имени могут быть присвоены определенные характеристики;
  •  код операции (КОП) и директива — это мнемонические обозначения соответствующей машинной команды, макрокоманды или директивы транслятора;
  •  операнды — части команды, макрокоманды или директивы ассемблера, обозначающие объекты, над которыми производятся действия. Операнды ассемблера описываются выражениями с числовыми и текстовыми константами, метками и идентификаторами переменных с использованием знаков операций и некоторых зарезервированных слов.

Как использовать синтаксические диаграммы? 

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

Допустимыми символами при написании текста программ являются:

  1.  все латинские буквы: A—Z, a—z. При этом заглавные и строчные буквы считаются эквивалентными;
  2.  цифры от 0 до 9;
  3.  знаки ?, @, $, _, &;
  4.  разделители , . [ ] ( ) < > { } + / * % ! ' " ? \ = # ^.

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

Лексемами являются:

  •  идентификаторы — последовательности допустимых символов, использующиеся для обозначения таких объектов программы, как коды операций, имена переменных и названия меток. Правило записи идентификаторов заключается в следующем: идентификатор может состоять из одного или нескольких символов. В качестве символов можно использовать буквы латинского алфавита, цифры и некоторые специальные знаки — _, ?, $, @. Идентификатор не может начинаться символом цифры. Длина идентификатора может быть до 255 символов, хотя транслятор воспринимает лишь первые 32, а остальные игнорирует. Регулировать длину возможных идентификаторов можно с использованием опции командной строки mv. Кроме этого существует возможность указать транслятору на то, чтобы он различал прописные и строчные буквы либо игнорировал их различие (что и делается по умолчанию). Для этого применяются опции командной строки /mu, /ml, /mx;
  •  цепочки символов — последовательности символов, заключенные в одинарные или двойные кавычки;
  •  целые числа в одной из следующих систем счисления: двоичной, десятичной, шестнадцатеричной. Отождествление чисел при записи их в программах на ассемблере производится по определенным правилам:
    •  Десятичные числа не требуют для своего отождествления указания каких-либо дополнительных символов, например 25 или 139.
    •  Для отождествления в исходном тексте программы двоичных чисел необходимо после записи нулей и единиц, входящих в их состав, поставить латинское “b”, например 10010101b.
    •  Шестнадцатеричные числа имеют больше условностей при своей записи:
      •  Во-первых, они состоят из цифр 0...9, строчных и прописных букв латинского алфавита a, b, c, d, e, f или A, B, C, D, E, F.
      •  Во-вторых, у транслятора могут возникнуть трудности с распознаванием шестнадцатеричных чисел из-за того, что они могут состоять как из одних цифр 0...9 (например 190845), так и начинаться с буквы латинского алфавита (например ef15). Для того чтобы "объяснить" транслятору, что данная лексема не является десятичным числом или идентификатором, программист должен специальным образом выделять шестнадцатеричное число. Для этого на конце последовательности шестнадцатеричных цифр, составляющих шестнадцатеричное число, записывают латинскую букву “h”. Это обязательное условие. Если шестнадцатеричное число начинается с буквы, то перед ним записывается ведущий ноль: 0ef15h.

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

Практически каждое предложение содержит описание объекта, над которым или при помощи которого выполняется некоторое действие. Эти объекты называются операндами.

Их можно определить так:

операнды — это объекты (некоторые значения, регистры или ячейки памяти), на которые действуют инструкции или директивы, либо это объекты, которые определяют или уточняют действие инструкций или директив. 

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

Возможно провести следующую классификацию операндов:

· постоянные, или непосредственные, операнды

· адресные операнды

· перемещаемые операнды

· счетчик адреса

· регистровый операнд

· базовый и индексный операнды

· структурные операнды

· Записи

Рассмотрим подробнее характеристику операндов из приведенной классификации:

  •  Постоянные или непосредственные операнды — число, строка, имя или выражение, имеющие некоторое фиксированное значение. Имя не должно быть перемещаемым, то есть зависеть от адреса загрузки программы в память. К примеру, оно может быть определено операторами equ или =.

 

num     equ     5

imd = num-2

        mov     al,num  ;эквивалентно mov al,5 

;5 здесь непосредственный операнд

        add     [si],imd        ; imd=3 - непосредственный операнд

        mov     al,5            ;5 - непосредственный операнд

        

  •  В данном фрагменте определяются две константы, которые затем используются в качестве непосредственных операндов в командах пересылки mov и сложения add. 
  •  Адресные операнды — задают физическое расположение операнда в памяти с помощью указания двух составляющих адреса: сегмента и смещения (рис. 4).

Рис. 4. Синтаксис описания адресных операндов

К примеру:

 

        mov     ax,0000h

        mov     ds,ax

        mov     ax,ds:0000h     ;записать слово в ax из области памяти по

                                        ;физическому адресу 0000:0000

        

Здесь третья команда mov имеет адресный операнд. 

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

К примеру:

  

data    segment

mas_w   dw      25 dup (0)

code    segment

        lea     si,mas_w        ;mas_w - перемещаемый операнд

 

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

  •  Счетчик адреса — специфический вид операнда. Он обозначается знаком $. Специфика этого операнда в том, что когда транслятор ассемблера встречает в исходной программе этот символ, то он подставляет вместо него текущее значение счетчика адреса. Значение счетчика адреса, или, как его иногда называют, счетчика размещения, представляет собой смещение текущей машинной команды относительно начала сегмента кода.  В формате листинга счетчику адреса соответствует вторая или третья колонка (в зависимости от того, присутствует или нет в листинге колонка с уровнем вложенности). Если взять в качестве пример любой листинг, то видно, что при обработке транслятором очередной команды ассемблера счетчик адреса увеличивается на длину сформированной машинной команды. Важно правильно понимать этот момент.

К примеру, обработка директив ассемблера не влечет за собой изменения счетчика. Директивы, в отличие от команд ассемблера, — это лишь указания транслятору на выполнение определенных действий по формированию машинного представления программы, и для них транслятором не генерируется никаких конструкций в памяти. В качестве примера использования в команде значения счетчика адреса можно привести следующий:

  

 jmp $+3 ;безусловный переход на команду mov

 cld ;длина команды cld составляет 1 байт

 mov al,1

  

  •  При использовании подобного выражения для перехода не забывайте о длине самой команды, в которой это выражение используется, так как значение счетчика адреса соответствует смещению в сегменте команд данной, а не следующей за ней команды. В нашем примере команда jmp занимает 2 байта. Но будьте осторожны, длина команды зависит от того, какие в ней используются операнды. Команда с регистровыми операндами будет короче команды, один из операндов которой расположен в памяти. В большинстве случаев эту информацию можно получить, зная формат машинной команды и анализируя колонку листинга с объектным кодом команды. 
  •  Регистровый операнд — это просто имя регистра. В программе на ассемблере можно использовать имена всех регистров общего назначения и большинства системных регистров.

   

 mov al,4 ;константу 4 заносим в регистр al

 mov dl,pass+4 ;байт по адресу pass+4 в регистр

dl

 add al,dl ;команда с регистровыми

операндами

  •  Базовый и индексный операнды. Этот тип операндов используется для реализации косвенной базовой, косвенной индексной адресации или их комбинаций и расширений. 
  •  Структурные операнды используются для доступа к конкретному элементу сложного типа данных, называемого структурой
  •  Записи (аналогично структурному типу) используются для доступа к битовому полю некоторой записи.

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

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

Выражения представляют собой комбинации операндов и операторов, рассматриваемые как единое целое.

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

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

· Арифметические операторы

· Операторы сдвига

· Операторы сравнения

· Логические операторы

· Индексный оператор

· Оператор переопределения типа

· Оператор переопределения сегмента

· Оператор именования типа структуры

· Оператор получения сегментной составляющей адреса выражения

· Оператор получения смещения выражения

 

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

  •  Арифметические операторы. К ним относятся:
    •  унарные “+” и “”;
    •  бинарные “+” и “”;
    •  умножения “*”;
    •  целочисленного деления “/”;
    •  получения остатка от деления “mod”.

Эти операторы расположены на уровнях приоритета 6, 7, 8 в табл. 2. Например,

    

tab_size equ 50 ;размер массива в байтах

size_el equ 2 ;размер элементов

;вычисляется число элементов массива и заносится в

регистр cx

 mov cx,tab_size / size_el ;оператор “/”

Рис. 5. Синтаксис арифметических операций

  •  Операторы сдвига выполняют сдвиг выражения на указанное количество разрядов (рис. 6). Например,

    

mask_b equ 10111011

mov al,mask_b shr 3 ;al=00010111

  •  
  •  Рис. 6. Синтаксис операторов сдвига
  •  Операторы сравнения (возвращают значение “истина” или “ложь”) предназначены для формирования логических выражений (см. рис. 7 и табл. 1). Логическое значение “истина” соответствует цифровой единице, а “ложь” — нулю. Например,

     

tab_size equ 30 ;размер таблицы

 mov al,tab_size ge 50 ;загрузка размера

таблицы в al

 cmp al,0 ;если tab_size < 50, то

 je m1 ;переход на m1

m1: …

  •  В этом примере если значение tab_size больше или равно 50, то результат в al равен 0ffh, а если tab_size меньше 50, то al равно 00h. Команда cmp сравнивает значение al с нулем и устанавливает соответствующие флаги в flags/eflags. Команда je на основе анализа этих флагов передает или не передает управление на метку m1.
  •  
  •  Рис. 7. Синтаксис операторов сравнения
  •  Таблица 1. Операторы сравнения

Оператор

Значение

eq

ИСТИНА, если выражение_1 равно выражение_2

ne

ИСТИНА, если выражение_1 не равно выражение_2

lt

ИСТИНА, если выражение_1 меньше выражение_2>ИСТИНА, если выражение_1 не равно выражение_2

le

ИСТИНА, если выражение_1 меньше или равно выражение_2

gt

ИСТИНА, если выражение_1 больше выражение_2

ge

ИСТИНА, если выражение_1 больше или равно выражение_2

  •  Логические операторы выполняют над выражениями побитовые операции (рис. 8). Выражения должны быть абсолютными, то есть такими, численное значение которых может быть вычислено транслятором. Например:

      

flags   equ     10010011

         mov    al,flags xor 01h        ;al=10010010;пересылка в al поля flags с

                                        ;инвертированным правым битом

  •  
  •  Рис. 8. Синтаксис логических операторов
  •  Индексный оператор [ ]. Не удивляйтесь, но скобки тоже являются оператором, и транслятор их наличие воспринимает как указание сложить значение выражение_1 за этими скобками с выражение_2, заключенным в скобки (рис. 9). Например,

        mov     ax,mas[si]      ;пересылка слова по адресу mas+(si) в регистр ax

  •  
  •  Рис. 9. Синтаксис индексного оператора
  •  Заметим, что в литературе по ассемблеру принято следующее обозначение: когда в тексте речь идет о содержимом регистра, то его название берут в круглые скобки. Мы также будем придерживаться этого обозначения.
  •  К примеру, в нашем случае запись в комментариях последнего фрагмента программы mas + (si) означает вычисление следующего выражения: значение смещения символического имени mas плюс содержимое регистра si.
  •  Оператор переопределения типа ptr применяется для переопределения или уточнения типа метки или переменной, определяемых выражением (рис. 10).
  •  Тип может принимать одно из следующих значений: byte, word, dword, qword, tbyte, near, far. Например,

d_wrd   dd      0

...

        mov     al,byte ptr d_wrd+1 ;пересылка второго байта из двойного слова

  •  Поясним этот фрагмент программы. Переменная d_wrd имеет тип двойного слова. Что делать, если возникнет необходимость обращения не ко всей переменной, а только к одному из входящих в нее байтов (например, ко второму)? Если попытаться сделать это командой
  •  mov al,d_wrd+1, то транслятор выдаст сообщение о несовпадении типов операндов. Оператор ptr позволяет непосредственно в команде переопределить тип и выполнить команду.
  •  
  •  Рис. 10. Синтаксис оператора переопределения типа
  •  Оператор переопределения сегмента : (двоеточие) заставляет вычислять физический адрес относительно конкретно задаваемой сегментной составляющей: “имя сегментного регистра”, “имя сегмента” из соответствующей директивы SEGMENT или “имя группы” (рис. 11). Этот момент очень важен, поэтому поясню его подробнее. При обсуждении сегментации мы говорили о том, что микропроцессор на аппаратном уровне поддерживает три типа сегментов — кода, стека и данных. В чем заключается такая аппаратная поддержка? К примеру, для выборки на выполнение очередной команды микропроцессор должен обязательно посмотреть содержимое сегментного регистра cs и только его. А в этом регистре, как мы знаем, содержится (пока еще не сдвинутый) физический адрес начала сегмента команд. Для получения адреса конкретной команды микропроцессору остается умножить содержимое cs на 16 (что означает сдвиг на четыре разряда) и сложить полученное 20-битное значение с 16-битным содержимым регистра ip. Примерно то же самое происходит и тогда, когда микропроцессор обрабатывает операнды в машинной команде. Если он видит, что операнд — это адрес (эффективный адрес, который является только частью физического адреса), то он знает, в каком сегменте его искать — по умолчанию это сегмент, адрес начала которого записан в сегментном регистре ds.
  •  А что же с сегментом стека? Посмотрите раздел "Программная модель микропроцессора", там, где мы описывали назначение регистров общего назначения.
  •  В контексте нашего рассмотрения нас интересуют регистры sp и bp. Если микропроцессор видит в качестве операнда (или его части, если операнд — выражение) один из этих регистров, то по умолчанию он формирует физический адрес операнда используя в качестве его сегментной составляющей содержимое регистра ss. Что подразумевает термин “по умолчанию”? Вспомните “рефлексы”, о которых мы говорили на уроке 1. Это набор микропрограмм в блоке микропрограммного управления, каждая из которых выполняет одну из команд в системе машинных команд микропроцессора. Каждая микропрограмма работает по своему алгоритму. Изменить его, конечно же, нельзя, но можно чуть-чуть подкорректировать. Делается это с помощью необязательного поля префикса машинной команды (см. формат машинной команды). Если мы согласны с тем, как работает команда, то это поле отсутствует. Если же мы хотим внести поправку (если, конечно, она допустима для конкретной команды) в алгоритм работы команды, то необходимо сформировать соответствующий префикс.
  •  Префикс представляет собой однобайтовую величину, численное значение которой определяет ее назначение. Микропроцессор распознает по указанному значению, что этот байт является префиксом, и дальнейшая работа микропрограммы выполняется с учетом поступившего указания на корректировку ее работы. Сейчас нас интересует один из них - префикс замены (переопределения) сегмента. Его назначение состоит в том, чтобы указать микропроцессору (а по сути, микропрограмме) на то, что мы не хотим использовать сегмент по умолчанию. Возможности для подобного переопределения, конечно, ограничены. Сегмент команд переопределить нельзя, адрес очередной исполняемой команды однозначно определяется парой cs:ip. А вот сегменты стека и данных — можно. Для этого и предназначен оператор “:”. Транслятор ассемблера, обрабатывая этот оператор, формирует соответствующий однобайтовый префикс замены сегмента. Например,

.code

...

        jmp     met1    ;обход обязателен, иначе поле ind

будет трактоваться ;как очередная команда

ind     db      5       ;описание поля данных в сегменте команд

met1:

...

mov al,cs:ind ;переопределение сегмента позволяет работать с

                        ;данными, определенными внутри сегмента кода

  •  
  •  Рис. 11. Синтаксис оператора переопределения сегмента
  •  Оператор именования типа структуры . (точка) также заставляет транслятор производить определенные вычисления, если он встречается в выражении. 
  •  Оператор получения сегментной составляющей адреса выражения seg возвращает физический адрес сегмента для выражения (рис. 12), в качестве которого могут выступать метка, переменная, имя сегмента, имя группы или некоторое символическое имя.

Рис. 12. Синтаксис оператора получения сегментной составляющей

  •  Оператор получения смещения выражения offset позволяет получить значение смещения выражения (рис. 13) в байтах относительно начала того сегмента, в котором выражение определено.

Рис. 13. Синтаксис оператора получения смещения

Например,

 

.data

pole    dw      5

...

.code

...

        mov     ax,seg  pole

        mov     es,ax

        mov     dx,offset       pole    ;теперь в паре es:dx полный адрес pole

 

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

Таблица 2. Операторы и их приоритет

Оператор

Приоритет

length, size, width, mask, (, ), [, ], <, >

1

.

2

:

3

ptr, offset, seg, type, this

4

high, low

5

+, - (унарные)

6

*, /, mod, shl, shr

7

+, -, (бинарные)

8

eq, ne, lt, le, gt, ge

9

not

10

and

11

or, xor

12

short, type

13

Директивы сегментации

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

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

  •  с одним сегментом кода;
  •  с одним сегментом стека;
  •  с одним сегментом данных;
  •  с тремя дополнительными сегментами данных.

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

Синтаксическое описание сегмента на ассемблере представляет собой конструкцию, изображенную на рис. 14:

Рис. 14. Синтаксис описания сегмента

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

Рассмотрим их подробнее.

  •  Атрибут выравнивания сегмента (тип выравнивания) сообщает компоновщику о том, что нужно обеспечить размещение начала сегмента на заданной границе. Это важно, поскольку при правильном выравнивании доступ к данным в процессорах i80х86 выполняется быстрее. Допустимые значения этого атрибута следующие:
    •  BYTE — выравнивание не выполняется. Сегмент может начинаться с любого адреса памяти;
    •  WORD — сегмент начинается по адресу, кратному двум, то есть последний (младший) значащий бит физического адреса равен 0 (выравнивание на границу слова);
    •  DWORD — сегмент начинается по адресу, кратному четырем, то есть два последних (младших) значащих бита равны 0 (выравнивание на границу двойного слова);
    •  PARA — сегмент начинается по адресу, кратному 16, то есть последняя шестнадцатеричная цифра адреса должна быть 0h (выравнивание на границу параграфа);
    •  PAGE — сегмент начинается по адресу, кратному 256, то есть две последние шестнадцатеричные цифры должны быть 00h (выравнивание на границу 256-байтной страницы);
    •  MEMPAGE — сегмент начинается по адресу, кратному 4 Кбайт, то есть три последние шестнадцатеричные цифры должны быть 000h (адрес следующей 4-Кбайтной страницы памяти).

По умолчанию тип выравнивания имеет значение PARA.

  •    Атрибут комбинирования сегментов (комбинаторный тип) сообщает компоновщику, как нужно комбинировать сегменты различных модулей, имеющие одно и то же имя. Значениями атрибута комбинирования сегмента могут быть:
    •  PRIVATE — сегмент не будет объединяться с другими сегментами с тем же именем вне данного модуля;
    •  PUBLIC — заставляет компоновщик соединить все сегменты с одинаковыми именами. Новый объединенный сегмент будет целым и непрерывным. Все адреса (смещения) объектов, а это могут быть, в зависимости от типа сегмента, команды и данные, будут вычисляться относительно начала этого нового сегмента;
    •  COMMON — располагает все сегменты с одним и тем же именем по одному адресу. Все сегменты с данным именем будут перекрываться и совместно использовать память. Размер полученного в результате сегмента будет равен размеру самого большого сегмента;
    •  AT xxxx — располагает сегмент по абсолютному адресу параграфа (параграф — объем памяти, кратный 16; поэтому последняя шестнадцатеричная цифра адреса параграфа равна 0). Абсолютный адрес параграфа задается выражением xxx. Компоновщик располагает сегмент по заданному адресу памяти (это можно использовать, например, для доступа к видеопамяти или области ПЗУ), учитывая атрибут комбинирования. Физически это означает, что сегмент при загрузке в память будет расположен, начиная с этого абсолютного адреса параграфа, но для доступа к нему в соответствующий сегментный регистр должно быть загружено заданное в атрибуте значение. Все метки и адреса в определенном таким образом сегменте отсчитываются относительно заданного абсолютного адреса;
    •  STACK — определение сегмента стека. Заставляет компоновщик соединить все одноименные сегменты и вычислять адреса в этих сегментах относительно регистра ss. Комбинированный тип STACK (стек) аналогичен комбинированному типу PUBLIC, за исключением того, что регистр ss является стандартным сегментным регистром для сегментов стека. Регистр sp устанавливается на конец объединенного сегмента стека. Если не указано ни одного сегмента стека, компоновщик выдаст предупреждение, что стековый сегмент не найден. Если сегмент стека создан, а комбинированный тип STACK не используется, программист должен явно загрузить в регистр ss адрес сегмента (подобно тому, как это делается для регистра ds).

По умолчанию атрибут комбинирования принимает значение PRIVATE.

  •    Атрибут класса сегмента (тип класса) — это заключенная в кавычки строка, помогающая компоновщику определить соответствующий порядок следования сегментов при собирании программы из сегментов нескольких модулей. Компоновщик объединяет вместе в памяти все сегменты с одним и тем же именем класса (имя класса, в общем случае, может быть любым, но лучше, если оно будет отражать функциональное назначение сегмента). Типичным примером использования имени класса является объединение в группу всех сегментов кода программы (обычно для этого используется класс “code”). С помощью механизма типизации класса можно группировать также сегменты инициализированных и неинициализированных данных;
  •  Атрибут размера сегмента. Для процессоров i80386 и выше сегменты могут быть 16 или 32-разрядными. Это влияет, прежде всего, на размер сегмента и порядок формирования физического адреса внутри него. Атрибут может принимать следующие значения:
    •  USE16 — это означает, что сегмент допускает 16-разрядную адресацию. При формировании физического адреса может использоваться только 16-разрядное смещение. Соответственно, такой сегмент может содержать до 64 Кбайт кода или данных;
    •  USE32 — сегмент будет 32-разрядным. При формирования физического адреса может использоваться 32-разрядное смещение. Поэтому такой сегмент может содержать до 4 Гбайт кода или данных.

Все сегменты сами по себе равноправны, так как директивы SEGMENT и ENDS не содержат информации о функциональном назначении сегментов. Для того чтобы использовать их как сегменты кода, данных или стека, необходимо предварительно сообщить транслятору об этом, для чего используют специальную директиву ASSUME, имеющую формат, показанный на рис. 15. Эта директива сообщает транслятору о том, какой сегмент к какому сегментному регистру привязан. В свою очередь, это позволит транслятору корректно связывать символические имена, определенные в сегментах. Привязка сегментов к сегментным регистрам осуществляется с помощью операндов этой директивы, в которых имя_сегмента должно быть именем сегмента, определенным в исходном тексте программы директивой SEGMENT или ключевым словом nothing. Если в качестве операнда используется только ключевое слово nothing, то предшествующие назначения сегментных регистров аннулируются, причем сразу для всех шести сегментных регистров. Но ключевое слово nothing можно использовать вместо аргумента имя сегмента; в этом случае будет выборочно разрываться связь между сегментом с именем имя сегмента и соответствующим сегментным регистром (см. рис. 15).

Рис. 15. Директива ASSUME 

На уроке 3 мы рассматривали пример программы с директивами сегментации. Эти директивы изначально использовались для оформления программы в трансляторах MASM и TASM. Поэтому их называют стандартными директивами сегментации.

Для простых программ, содержащих по одному сегменту для кода, данных и стека, хотелось бы упростить ее описание. Для этого в трансляторы MASM и TASM ввели возможность использования упрощенных директив сегментации. Но здесь возникла проблема, связанная с тем, что необходимо было как-то компенсировать невозможность напрямую управлять размещением и комбинированием сегментов. Для этого совместно с упрощенными директивами сегментации стали использовать директиву указания модели памяти MODEL, которая частично стала управлять размещением сегментов и выполнять функции директивы ASSUME (поэтому при использовании упрощенных директив сегментации директиву ASSUME можно не использовать). Эта директива связывает сегменты, которые в случае использования упрощенных директив сегментации имеют предопределенные имена, с сегментными регистрами (хотя явно инициализировать ds все равно придется).

В листинге 1 приведен пример программы с использованием упрощенных директив сегментации:

 

        Листинг 1. Использование упрощенных директив сегментации

;---------Prg_3_1.asm-------------------------------

masm                    ;режим работы TASM: ideal или masm

model   small           ;модель памяти

.data                   ;сегмент данных

message db      'Введите две шестнадцатеричные цифры,$'

.stack                  ;сегмент стека

        db      256     dup ('?')       ;сегмент стека

.code                   ;сегмент кода

main    proc            ;начало процедуры main

        mov     ax,@data        ;заносим адрес сегмента данных в регистр ax

        mov     ds,ax   ;ax в ds

;далее текст программы (см. сегмента кода в листинге 3.1 книги)

        mov     ax,4c00h        ;пересылка 4c00h в регистр ax

        int     21h             ;вызов прерывания с номером 21h

main    endp            ;конец процедуры main

end     main            ;конец программы с точкой входа main

Синтаксис директивы MODEL показан на рис. 16.

Рис. 16. Синтаксис директивы MODEL

Обязательным параметром директивы MODEL является модель памяти. Этот параметр определяет модель сегментации памяти для программного модуля. Предполагается, что программный модуль может иметь только определенные типы сегментов, которые определяются упомянутыми нами ранее упрощенными директивами описания сегментов. Эти директивы приведены в табл. 3.

Таблица 3. Упрощенные директивы определения сегмента

Формат директивы 

(режим MASM)

Формат директивы 

(режим IDEAL)

Назначение

.CODE [имя]

CODESEG[имя]

Начало или продолжение сегмента кода

.DATA

DATASEG

Начало или продолжение сегмента инициализированных данных. Также используется для определения данных типа near

.CONST

CONST

Начало или продолжение сегмента постоянных данных (констант) модуля

.DATA?

UDATASEG

Начало или продолжение сегмента неинициализированных данных. Также используется для определения данных типа near

.STACK [размер]

STACK [размер]

Начало или продолжение сегмента стека модуля. Параметр [размер] задает размер стека

.FARDATA [имя]

FARDATA [имя]

Начало или продолжение сегмента инициализированных данных типа far

.FARDATA? [имя]

UFARDATA [имя]

Начало или продолжение сегмента неинициализированных данных типа far

Наличие в некоторых директивах параметра [имя] говорит о том, что возможно определение нескольких сегментов этого типа. С другой стороны, наличие нескольких видов сегментов данных обусловлено требованием обеспечить совместимость с некоторыми компиляторами языков высокого уровня, которые создают разные сегменты данных для инициализированных и неинициализированных данных, а также констант.

При использовании директивы MODEL транслятор делает доступными несколько идентификаторов, к которым можно обращаться во время работы программы, с тем, чтобы получить информацию о тех или иных характеристиках данной модели памяти (см. табл. 5). Перечислим эти идентификаторы и их значения (табл. 4).

Таблица 4. Идентификаторы, создаваемые директивой MODEL

Имя идентификатора

Значение переменной

@code

Физический адрес сегмента кода

@data

Физический адрес сегмента данных типа near

@fardata

Физический адрес сегмента данных типа far

@fardata?

Физический адрес сегмента неинициализированных данных типа far

@curseg

Физический адрес сегмента неинициализированных данных типа far

@stack

Физический адрес сегмента стека

Если вы посмотрите на текст листинга 1, то увидите пример использования одного из этих идентификаторов. Это @data; с его помощью мы получили значение физического адреса сегмента данных нашей программы.

Теперь можно закончить обсуждение директивы MODEL. Операнды директивы MODEL используют для задания модели памяти, которая определяет набор сегментов программы, размеры сегментов данных и кода, способ связывания сегментов и сегментных регистров. В табл. 5 приведены некоторые значения параметра модель памяти директивы MODEL

Таблица 5. Модели памяти

Модель

Тип кода

Тип данных

Назначение модели

TINY

near

near

Код и данные объединены в одну группу с именем DGROUP. 

Используется для создания программ формата .com.

SMALL

near

near

Код занимает один сегмент, данные объединены в одну группу с именем DGROUP. 

Эту модель обычно используют для большинства программ на ассемблере

MEDIUM

far

near

Код занимает несколько сегментов, по одному на каждый объединяемый программный модуль. 

Все ссылки на передачу управления — типа far. 

Данные объединены в одной группе; все ссылки на них — типа near

COMPACT

near

far

Код в одном сегменте; 

ссылка на данные — типа far

LARGE

far

far

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

Параметр модификатор директивы MODEL позволяет уточнить некоторые особенности использования выбранной модели памяти (табл. 6).

Таблица 6. Модификаторы модели памяти

Значение модификатора

Назначение

use16

Сегменты выбранной модели используются как 16-битные (если соответствующей директивой указан процессор i80386 или i80486)

use32

Сегменты выбранной модели используются как 32-битные (если соответствующей директивой указан процессор i80386 или i80486)

dos

Программа будет работать в MS-DOS

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

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

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


Лекция 4. Описание системы команд микропроцессоров Intel (2 пары + ср)

Материал, приведенный в данном разделе справочной системы, связан с уроком 6, на котором мы рассматривали формат машинной команды микропроцессора и систему его команд в целом.

Выберите тему:

Знакомство с порядком описания команд и принятыми обозначениями 

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

Описание команд микропроцессора, упорядоченное по функциональному признаку 

 

 

 

 

aaa

aad

aam

aas

adc

add

and

bound

bsf

bsr

bswap

bt

btc

btr

bts

call

cbw

cwde

clc

cld

cli

cmc

cmp

cmps/cmpsb 

/cmpsw/cmpsd

cmpxchg

cwd

cdq

daa

das

dec

div

enter

hlt

idiv

imul

in

inc

ins/insb 

/insw/insd

int

into

iret/iretd

jcc

jcxz

jecxz

jmp

lahf

lds

les

lfs

lgs

lss

lea

leave

lgdt

lidt

lods/lodsb 

/lodsw/lodsd

loop

loope

loopz

loopne

loopnz

mov

movs/movsb 

/movsw/movsd

movsx

movzx

mul

neg

nop

not

or

out

outs

pop

popa

popad

popf

popfd

push

pusha

pushad

pushf

pushfd

rcl

rcr

rep/repe/repz 

/repne/repnz

ret/retf

rol

ror

sahf

sal

sar

sbb

scas/scasb 

/scasw/scasd

setcc

sgdt

sidt

shl

shld

shr

shrd

stc

std

sti

stos/stosb 

/stosw/stosd

sub

test

xadd

xchg

xlat/xlatb

xor

 

 

 

 

 

 

Порядок описания команд будет следующим:

 

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

Для описания команд приняты обозначения:

  1.  Для описания состояния флагов после выполнения некоторой команды будем использовать выборку из таблицы, отражающей структуру регистра флагов eflags:

31

18

17

16

15

14

1312

11

10

09

08

07

06

05

04

03

02

01

00

0

0

VM

RF

0

NT

IOPL

OF

DF

IF

TF

SF

ZF

0

AF

0

PF

1

CF

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  1.  В нижней строке этой таблицы приводятся значения флагов после выполнения команды. При этом используются следующие обозначения:
    •  1 — после выполнения команды флаг устанавливается (равен 1);
    •  0 — после выполнения команды флаг сбрасывается (равен 0);
    •  r — значение флага зависит от результата работы команды;
    •  ? — после выполнения команды флаг не определен;
    •  пробел — после выполнения команды флаг не изменяется;
  2.  Для представления операндов в синтаксических диаграммах используются следующие обозначения:
    •  r8, r16, r32 — операнд в одном из регистров размером байт, слово или двойное слово;
    •  m8, m16, m32, m48 — операнд в памяти размером байт, слово, двойное слово или 48 бит;
    •  i8, i16, i32 — непосредственный операнд размером байт, слово или двойное слово;
    •  a8, a16, a32 — относительный адрес (смещение) в сегменте кода.
  3.  На многих диаграммах в целях компактности возможные сочетания операндов показаны в виде следующей конструкции:

Конструируя команду на основе подобной синтаксической диаграммы, вы должны помнить о соответствии типов. В подобной диаграмме допустимы только следующие сочетания: "r8, m8", "r16, m16", "r32, m32". Например, сочетание "r8, m16" недопустимо. Однако есть единичные случаи, когда подобные сочетания возможны; тогда они специально оговариваются.

  1.  Описанная в данном приложении система команд в полном объеме поддерживается микропроцессором Pentium. Предыдущие модели микропроцессора могут не поддерживать отдельные команды. Чтобы прояснить этот момент, мы будем указывать в примерах для каждой команды директиву типа .286. Это будет означать, что описываемая команда поддерживается всеми моделями микропроцессора, начиная с i286. Если ничего не указывается, то это означает, что данная команда работает на всех моделях микропроцессоров Intel, начиная с i8086/8088.

AAA

(Ascii Adjust after Addition)

ASCII-коррекция после сложения

 

Схема команды: 

aaa 

Назначение: корректировка неупакованного результата сложения двух одноразрядных неупакованных BCD-чисел.

Синтаксис 

Алгоритм работы: 

  •  проанализировать значение младшего полубайта регистра al и значение флага af;
  •  если (значение младшего полубайта регистра al >9) или (AF=1), то выполнить следующие действия:
    •  увеличить значение al на 6;
    •  очистить старший полубайт регистра al;
    •  увеличить значение ah на 1;
    •  установить флаги: af = 1, cf = 1,

иначе сбросить флаги af = 0 и cf = 0.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

?

r

?

r

Применение: 

Обычно команда aaa используется после сложения каждого разряда распакованных BCD-чисел командой add. Каждая цифра неупакованного BCD-числа занимает младший полубайт байта. Если результат сложения двух одноразрядных BCD-чисел больше 9, то число в младшем полубайте результата не есть BCD-число. Поэтому результат нужно корректировать командой aaa. Эта команда позволяет сформировать правильное BCD-число в младшем полубайте и запомнить единицу переноса в старший разряд путем увеличения содержимого регистра ah на 1.

К примеру, сложить два неупакованных BCD-числа: 08 + 05:

        mov     ah,08h  ;ah=08h

        mov     al,05h  ;al=05h

        add     al,ah   ;al=al+ah=05h+08h=0dh — не BCD-число

        xor     ah,ah   ;ah=0

        aaa             ;ah=01h,al=03h — результат скорректирован

        

См. также: урок 8, приложение 7 и команды aad, aam, aas, daa, das

AAD

(Ascii Adjust before Division)

ASCII-коррекция перед делением

 

Схема команды: 

aad 

Назначение: 

  •  подготовкa двух неупакованных BCD-чисел для операции деления;
  •  преобразование двузначного неупакованного ВCD-числа меньшего 63h (9910) в двоичное представление.

Синтаксис 

Алгоритм работы: 

  •  умножить значение регистра ah на 10 и сложить полученное значение с содержимым регистра al: (ah*10)+al;
  •  присвоить регистру al значение (ah*10)+al;
  •  обнулить регистр ah.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

?

?

Применение: 

Команду aad используют для подготовки двузначного неупакованного BCD-числа в регистре ax для операции деления. Так как в системе команд микропроцессора нет команды деления для BCD-чисел, такое число нужно предварительно преобразовать в двоичный вид. Для этого старший разряд двузначного BCD-числа помещается в регистр ah, умножается на 10 и складывается с разрядом единиц двузначного BCD-числа 9 в регистре al. В результате этих действий и получается соответствующее двоичное число в регистре ax. Далее в программе уже можно применять обычную команду деления div, оперирующую двоичными данными. Команду aad можно применять и просто для преобразования неупакованного двузначного BCD-числа в его двоичный эквивалент. Есть еще интересный момент — если посмотреть на коды символов шестнадцатеричных цифр в таблице ASCII, то видно, что они похожи на BCD-числа. Исключение составляет лишь значение старшей тетрады (для BCD-числа это так называемая зона с нулевым значением) - оно равно 3. Можно сделать вывод, что если предварительно обнулить значение старшей тетрады для кодов двух символов (от 0 до 9), то эту команду вполне можно применять и для преобразования двузначных десятичных чисел в символьном представлении в их двоичный эквивалент, что и отражено в названии команды. Для иллюстрации рассмотрим два примера.

Пример 1. Разделить десятичное число 18 на 9. Подготовить результат к выводу на экран.

        mov     ah,01h  ;ah=01h

        mov     al,08h  ;al=08h => ax=0108h

        mov     bl,09   ;bl=09h

        aаd             ;al=12h — двоичный эквивалент десятичного числа 18

        div     bl      ;al=02h,ah=00h

        ог      al,30h  ;al=32h — ASCII-представление числа 2, можно выводить на экран

        

Пример 2. Преобразовать десятичное число 16 в символьном виде в эквивалентное двоичное число.

        mov     ax,3136h        ;ax=3136h

        and     ax,0f0fh        ;ax=0106h

        aаd             ;al=10h — получили его двоичный эквивалент

        

См. также: уроки 3, 8, приложение 7 и команды aaa, aam, aas, daa, das

AAM

(Ascii Adjust after Multiply)

ASCII-коррекция после умножения

 

Схема команды:

aam

Назначение: 

  •  корректировка результата умножения двух неупакованных BCD-чисел;
  •  преобразование двоичного числа меньшего 63h (9910) в его неупакованный BCD-эквивалент.

Синтаксис 

Алгоритм работы: 

  •  разделить значение регистра al на 10;
  •  записать частное в регистр ah, остаток — в регистр al.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

?

?

Применение: 

Команду aam используют для коррекции результата умножения двух неупакованных BCD-чисел. Специальной команды умножения BCD-чисел нет. Поэтому BCD-числа умножаются поразрядно, как обычные двоичные числа, командой mul. Максимальное число, которое получается при таком умножении, — это 9*9=8110=5116. Отсюда понятно, что значения, для которых командой aam можно получить их двузначный BCD-эквивалент в регистре ax, находятся в дипазоне от 00h до 51h. Эту команду можно применять и для преобразования двоичного числа из регистра ax (в диапазоне от 0 до 63h) в его десятичный эквивалент(соответственно, из диапазона от 0 до 9910).

Пример 1. Умножить десятичное число 8 на 9. Подготовить результат к выводу на экран.

        mov     ah,08h  ;ah=08h

        mov     al,09h  ;al= 09h

        mul     ah      ;al=48h — двоичный эквивалент 72

        aam             ;ah=07h,al=02h

        or      ax,3030h        ;ax=3732h — ASCII-представление числа 72

Пример 2. Преобразовать двоичное число 60h в эквивалентное десятичное число.

;поместим число 60h в регистр ax

        mov     ax,60h  ;ax=60h

        aаm             ;ax=0906h — получили десятичный эквивалент числа 60h

        or      ax,3030h        ;символьный эквивалент, можно выводить на экран

См. также: урок 8, приложение 7 и команды aaa, aad, aas, daa, das

AAS

(Ascii Adjust after Substraction)

ASCII-коррекция после вычитания

 

Схема команды: 

aas 

Назначение: корректировка результата вычитания двух неупакованных одноразрядных BCD-чисел.

Синтаксис 

Алгоритм работы: 

если (младший полубайт регистра al меньше 9) или (флаг af=1), то выполнить следующие действия:

  •  уменьшить значение младшего полубайта регистра al на 6;
  •  обнулить значение старшего полубайта регистра al;
  •  установить флаги af и cf в 1;

иначе установить флаги af и cf в 1.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

?

r

r

Применение: 

Команду aas используют для коррекции результата вычитания двух неупакованных одноразрядных BCD-чисел после команды sub. Операндами в команде sub должны быть правильные одноразрядные BCD-числа. Рассмотрим возможные варианты вычитания одноразрядных BCD-чисел:

  •  5-9 — для вычитания необходимо сделать заем в старшем разряде. Факт такого заема в микропроцессоре фиксируется установкой флагов cf и af в 1 и вычитанием 1 из содержимого ah. В результате после команды aas в регистре al получается правильное значение (модуль результата), которое для нашего примера (с учетом заема из старшего разряда) составляет 6. Одновременно моделируется заем из старшего разряда, что позволяет производить вычитание длинных чисел.
  •  8-6 — для вычитания нет необходимости делать заем в старшем разряде. Поэтому производится сброс флагов cf и af в 0, а ah не изменяется. В результате после команды aas в регистре al получается правильное значение (модуль результата), которое для нашего примера составляет 2.

Пример 1. Вычесть десятичное число 8 из 5. Подготовить результат к выводу на экран.

        mov     al,05h

        mov     bl,08h

        sub     al,bl   ;al=0fdh

        aas             ;al=07, cf=af=1

        or      al,30h  ;al=37h — код символа 7

;вывод результата на экран

        mov     ah,2

        mov     dl,al

        int     21h

        

См. также: уроки 3, 8, приложение 7 и команды aaa, aad, aam, daa, das

ADC

(Addition with Carry)

Сложение с переносом

 

Схема команды: 

adc приемник,источник 

Назначение: сложение двух операндов с учетом переноса из младшего разряда.

Синтаксис 

Алгоритм работы: 

  •  сложить два операнда;
  •  поместить результат в первый операнд: приемник=приемник+источник;
  •  в зависимости от результата установить флаги.

Состояние флагов после выполнения команды: 

11

07

06

04

02

02

OF

SF

ZF

AF

PF

CF

r

r

r

r

r

Применение: 

Команда adc используется при сложении длинных двоичных чисел. Ее можно использовать как самостоятельно, так и совместно с командой add. При совместном использовании команды adc с командой add сложение младших байтов/слов/двойных слов осуществляется командой add, а уже старшие байты/слова/двойные слова складываются командой adc, учитывающей переносы из младших разрядов в старшие. Таким образом, команда adc значительно расширяет диапазон значений складываемых чисел. В приложении 7 приведен пример программы сложения двоичных чисел произвольной размерности.

.data

sl1     dd      01fe544fh

sl2     dd      005044cdh

elderREZ        db      0 ;для учета переноса из старшего разряда результата

rez     dd      0

.code

...

        mov     ax,sl1

        add     ax,sl2  ;сложение младших слов слагаемых

        mov     rez,ax

        mov     ax,sl+2

        adc     ax,sl2+2        ;сложение старших слов слагаемых плюс cf

        mov     rez+2,ax

        adc     elderREZ,0      ;учесть возможный перенос

        

См. также: урок 8, приложение 7 и команды add, sub, sbb, xadd

ADD

(ADDition)

Сложение

 

Схема команды: 

add приемник,источник 

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

Синтаксис 

Алгоритм работы: 

  •  сложить операнды источник и приемник;
  •  записать результат сложения в приемник;
  •  установить флаги.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

r

r

Применение: 

Команда add используется для сложения двух целочисленных операндов. Результат сложения помещается по адресу первого операнда. Если результат сложения выходит за границы операнда приемник (возникает переполнение), то учесть эту ситуацию следует путем анализа флага cf и последующего возможного применения команды adc. Например, сложим значения в регистре ax и области памяти ch. При сложении следует учесть возможность переполнения.

chiclo  dw      2015

rez     dd      0

...

        add     ax,chislo       ;(ax)=(ax)+ch

        mov     word ptr rez,ax

        jnc     dop_sum         ;переход, если результат не вышел за разрядную сетку

        adc     word ptr rez+2,0        ;расширить результат, для учета переноса

                                ;в старший разряд

dop_sum:

...

        

См. также: урок 8, Приложение 7 и команды adc, sub, sbb, xadd

AND

(logical AND)

Логическое И

 

Схема команды: 

and приемник,источник 

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

Синтаксис 

Алгоритм работы: 

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

Состояние флагов после выполнения команды: 

11

07

06

02

00

OF

SF

ZF

PF

CF

0

r

r

0

Применение: 

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

Например, преобразуем двузначное упакованное BCD-число в его символьный эквивалент.

u_BCD   db      25h ;упакованное BCD-число

s_ch    dw      0 ;место для результата

...

        xor     ax,ax   ;очистка ax

        mov     al,u_BCD

        shl     ax,4    ;ax=0250

        mov     al,u_BCD        ;ax=0225

;преобразование в символьное представление:

        and     ax,3f3fh        ;ax=3235h

        mov     s_ch,ax

        

См. также: уроки 9, 12 и команды or, xor, test

BOUND

(check array BOUNDs)

Контроль нахождения индекса массива в границах

 

Схема команды: 

bound индекс,границы массива 

Назначение: проверка нахождения значения индекса в границах массива.

Синтаксис 

Алгоритм работы: 

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

  •  если это 16-разрядный регистр общего назначения, то содержащееся в нем значение проверяется на попадание в диапазон значений, которые находятся в двух последовательных словах в памяти по адресу, указываемому вторым операндом. Эти два значения являются, соответственно, значениями нижнего и верхнего индекса границы массива;
  •  если это 32-разрядный регистр общего назначения, то содержащееся в нем значение проверяется на попадание в диапазон значений, которые находятся в двух последовательных двойных словах в памяти по адресу, указываемому вторым операндом. Эти два значения являются, соответственно, значениями нижнего и верхнего индекса границы массива;

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

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

 

Применение: 

Команду bound очень удобно использовать для контроля выхода за нижнюю или верхнюю границы массива. Значения этих границ должны быть предварительно помещены в два последовательных слова (двойных слова) в памяти. Адрес этих слов (двойных слов) указывается вторым операндом. Далее динамически в ходе работы программы значение в регистре индекс, указываемом первым операндом, сравнивается со значениями этих двух границ, и если нижняя_граница<=(индексindex)<=верхняя_граница, то программа продолжает выполнение. В противном случае генерируется исключительная ситуация 5 (int 5). Далее в программе обработки этой ситуации можно выполнить необходимую корректировку и вернуться в программу (см. урок 17).

Фрагмент, который можно использовать при обработке одномерного массива с размерностью элементов в слово:

.286    ;это обязательная директива, так как bound

        ;входит в систему команд микропроцессоров, начиная с i286

.data

BoundMas        label   word

Low_Bound       dw      0

Upp_Bound       dw      20

mas     dw      10 dup (?)

...

        xor     di,di   ;очистка индексного регистра

cycl:

        mov     ax,mas[di]      ;перебор

элементов массива

        add     di,2

        bound   di,BoundMas

;если значение в di не будет попадать в границы, то будет вызван

;обработчик прерывания 5, где можно скорректировать

;значение ip/eip в стеке с тем, чтобы выйти

;из бесконечного ;цикла, например, на метку М2 или

;выполнить другие действия

        jmp     cycl

М2:

...

        

См. также: урок 17 и команду iret/iretd

BSF

(Bit Scan Forward)

Побитное сканирование вперед

 

Схема команды: 

bsf результат,источник 

Назначение: для проверки наличия единичных битов в операнде источник.

Синтаксис 

Алгоритм работы: 

  •  просмотр битов операнда источник, начиная с бита 0 и заканчивая битом 15/31, до тех пор, пока не встретится единичный бит;
  •  если встретился единичный бит, то флаг zf устанавливается в 0 и в регистр первого операнда записывается номер позиции, где встретился единичный бит. Диапазон значений зависит от разрядности второго операнда: для 16-разрядного операнда — это 0...15; для 32-разрядного — это 0...31;
  •  если единичных битов нет, то флаг zf устанавливается в 1.

Состояние флагов после выполнения команды: 

06

ZF

r

Применение: 

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

Например, сдвинем содержимое регистра bx вправо таким образом, чтобы нулевой бит стал единичным:

.386

        mov     bx,0002h        ;bx=0000 0010b

...

        bsf     cx,bx   ;cx=0001h

        jz      null

        shr     bx,cl   ;bx=0000 0001b

...

null:

        

См. также: урок 9, 12 и команду bsr

BSR

(Bit Scan Reverse)

Побитное сканирование назад

 

Схема команды: 

bsr результат,источник 

Назначение: проверка наличия единичных битов в операнде источник.

Синтаксис 

Алгоритм работы: 

  •  просмотр битов операнда источник, начиная со старшего бита 15/31 и заканчивая битом 0, до тех пор, пока не встретится единичный бит;
  •  если встретился единичный бит, флаг zf устанавливается в 0 и в регистр первого операнда записывается номер позиции (отсчет осуществляется относительно нулевой позиции), где встретился самый старший единичный бит. Диапазон значений зависит от разрядности второго операнда: для 16-разрядного операнда это 0...15; для 32-разрядного — 0...31;
  •  если единичных битов нет, флаг zf устанавливается в 1.

Состояние флагов после выполнения команды: 

06

ZF

r

Применение: 

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

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

.386

        mov     bx,41h

...

        bsr     cx,bx   ;cx=06h

        jz      null

        shr     bx,ax   ;bx=0001h

...

null:...

        

См. также: уроки 9, 12 и команду bsf

BSWAP

(Byte SWAP)

Перестановка байтов

 

Схема команды: 

bswap источник 

Назначение: 

  •  изменение порядка следования байтов;
  •  переход от одной формы адресации к другой.

Под формой адресации здесь понимается принцип "младший байт по младшему адресу" или обратный ему. Существует ряд систем, например использующих микропроцессоры Motorola или большие ЭВМ, где применяется принцип размещения многобайтовых значений обратный тому, который используется в микропроцессорах Intel. Поэтому эту команду можно использовать для разработки программ-конверторов между подобными платформами и IBM РС.

Синтаксис 

Алгоритм работы: Схема алгоритма 

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

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

.486

        mov     ebx,1a2c345fhh

        bswap   ebx     ;ebx=5f342c1ah

        

См. также: урок 7, и команду xchg

BT

(Bit Test)

Проверка битов

 

Схема команды: 

bt источник,индекс 

Назначение: извлечение значения заданного бита в флаг cf.

Синтаксис 

Алгоритм работы: 

  •  получить бит по указанному номеру позиции в операнде источник;
  •  установить флаг cf согласно значению этого бита.

Состояние флагов после выполнения команды: 

00

CF

r

Применение: 

Команду bt используют для определения значения конкретного бита в операнде источник. Номер проверяемого бита задается содержимым второго операнда (значение числом из диапазона 0...31). После выполнения команды, флаг cf устанавливается в соответствии со значением проверяемого бита.

.386

        mov     ebx,01001100h

        bt      ebx,8   ;проверка состояния бита 8 и установка cf= в 1

        jc      m1      ;перейти на m1, если проверяемый бит равен 1

...

        

См. также: уроки 9, 12 и команды btc, btr, bts, test

BTC

(Bit Test and Complement)

Проверка бита с инверсией (дополнением)

 

Схема команды: 

btc источник,индекс 

Назначение: извлечение значения заданного бита в флаг cf и изменение его значения в операнде на обратное.

Синтаксис 

Алгоритм работы: 

  •  получить значение бита с номером позиции индекс в операнде источник;
  •  инвертировать значение выбранного бита в операнде источник;
  •  установить флаг сf исходным значением бита.

Состояние флагов после выполнения команды: 

00

CF

r

Применение: 

Команда btс используется для определения и инвертирования значения конкретного бита в операнде источник. Номер проверяемого бита задается содержимым второго операнда индекс (значение из диапазона 0...31). После выполнения команды флаг cf устанавливается в соответствии с исходным значением бита, то есть тем, которое было до выполнения команды.

.386

        mov     ebx,01001100h

;проверка состояния бита 8 и его обращение:

        btc     ebx,8   ;cf=1 и ebx=01001000h

        

См. также: уроки 9, 12 и команды bt, btr, bts, test

BTR

(Bit Test and Reset)

Проверка бита с его сбросом в 0

 

Схема команды: 

btr источник,индекс 

Назначение: извлечение значения заданного бита в флаг cf и изменение его значения на нулевое.

Синтаксис 

Алгоритм работы: 

  •  получить значение бита с указанным номером позиции в операнде источник;
  •  установить флаг cf значением выбранного бита;
  •  установить значение исходного бита в операнде в 0.

Состояние флагов после выполнения команды: 

00

CF

r

Применение: 

Команда btr используется для определения значения конкретного бита в операнде источник и его сброса в 0. Номер проверяемого бита задается содержимым второго операнда индекс (значение из диапазона 0...31). В результате выполнения команды флаг cf устанавливается в соответствии со значением исходного бита, то есть тем, что было до выполнения операции.

.386

        mov     ebx,01001100h

;проверка состояния бита 8 и его сброс в 0

        btr     ebx,8   ;cf=1 и ebx=01001000h

        

См. также: уроки 9, 12 и команды bt, btc, bts, test

BTS

(Bit Test and Set)

Проверка бита с его установкой в 1

 

Схема команды: 

bts источник,индекс 

Назначение: извлечение значения заданного бита операнда в флаг cf и установка этого бита в единицу.

Синтаксис 

Алгоритм работы: 

  •  получить значение бита с указанным номером позиции в операнде источник;
  •  установить флаг cf значением выбранного бита;
  •  установить значение исходного бита в операнде источник в 1.

Состояние флагов после выполнения команды: 

00

CF

r

Применение: 

Команда bts используется для определения значения конкретного бита в операнде источник и установки проверяемого бита в 1. Номер проверяемого бита задается содержимым второго операнда индекс (значение из диапазона 0...31). После выполнения команды флаг cf устанавливается в соответствии со значением исходного бита, то есть тем, что было до выполнения операции.

.386

        mov     ebx,01001100h

;проверка состояния бита 0 и его установка в 1

        bts     ebx,0   ;cf=0 ebx=01001001h

        

См. также: уроки 9, 12 и команды bt, btc, btr, test

CALL

(CALL)

Вызов процедуры или задачи

 

Схема команды: 

call цель 

Назначение: 

  •  передача управления близкой или дальней процедуре с запоминанием в стеке адреса точки возврата;
  •  переключение задач.

Синтаксис 

Алгоритм работы: 

определяется типом операнда:

  •  метка ближняя — в стек заносится содержимое указателя команд eip/ip и в этот же регистр загружается новое значение адреса, соответствующее метке;
  •  метка дальняя — в стек заносится содержимое указателя команд eip/ip и cs. Затем в эти же регистры загружаются новые значения адресов, соответствующие дальней метке;
  •  r16, 32 или m16, 32 — определяют регистр или ячейку памяти, содержащие смещения в текущем сегменте команд, куда передается управление. При передаче управления в стек заносится содержимое указателя команд eip/ip;
  •  указатель на память — определяет ячейку памяти, содержащую 4 или 6-байтный указатель на вызываемую процедуру. Структура такого указателя 2+2 или 2+4 байта. Интерпретация такого указателя зависит от режима работы микропроцессора:
  •  в реальном режиме — в зависимости от размера адреса (use16 или use32) первые два байта трактуются как сегментный адрес, вторые два/четыре байта, как смещение целевой метки передачи управления. В стеке запоминается содержимое регистров cs и eip/ip;
  •  в защищенном режиме — интерпретация цели передачи управления зависит от значения байта AR дескриптора, определяемого селекторной частью указателя. Целью здесь являются дальний вызов процедуры без изменения уровня привилегий, дальний вызов процедуры с изменением уровня привилегий или переключение задачи.

Состояние флагов после выполнения команды (кроме переключения задачи): 

выполнение команды не влияет на флаги

При переключении задачи значения флажков изменяются в соответствии с информацией о регистре eflags в сегменте состояния TSS задачи, на которую производится переключение.

Применение: 

Как видно из описания алгоритма, команда call позволяет организовать гибкую и многовариантную передачу управления на подпрограмму с сохранением адреса точки возврата. Подробно типовые примеры использования рассмотрены на уроках 10 и 14.

См. также: уроки 10, 14 и команду ret 

CBW/CWDE

(Convert Byte to Word/Convert Word to Double Word Extended)

Преобразование байта в слово/слова в двойное слово

 

Схема команды: 

cbw 

cwde 

Назначение: расширение операнда со знаком.

Синтаксис 

Алгоритм работы: 

cbw — при работе команда использует только регистры al и ax:

  •  анализ знакового бита регистра al:
    •  если знаковый бит al=0, то ah=00h;
    •  если знаковый бит al=1, то ah=0ffh.

cwde — при работе команда использует только регистры ax и eax:

  •  анализ знакового бита регистра ax:
    •  если знаковый бит ax=0, то установить старшее слово eax=0000h;
    •  если знаковый бит ax=1, то установить старшее слово eax=0ffffh.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

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

.386    ;только для cwde, cwd была для i8086

        mov     ebx,10fecd23h

        mov     ax,-3   ;ax=1111 1111 1111 1101

        cwde    ;eax=1111 1111 1111 1111 1111 1111 1111 1101

        add     eax,ebx

        

См. также: урок 8 и команды cdq, cwd

CLC

(CLear Carry flag)

Сброс флага переноса

 

Схема команды: 

clc 

Назначение: сброс флага переноса cf.

Синтаксис 

Алгоритм работы: 

установка флага cf в ноль.

Состояние флагов после выполнения команды: 

00

CF

0

Применение: 

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

        clc             ;cf=0

        

См. также: уроки 8, 9 и команды cmc, stc

CLD

(CLear Direction flag)

Сброс флага направления

 

Схема команды: 

cld 

Назначение: сброс в ноль флага направления df.

Синтаксис 

Алгоритм работы: 

установка флага df в ноль.

Состояние флагов после выполнения команды: 

10

DF

0

Применение: 

Данная команда используется для сброса флага df в ноль. Такая необходимость может возникнуть при работе с цепочечными командами. Нулевое занчение флага df вынуждает микропроцессор при выполнении цепочечных операций производить инкремент регистров si и di.

        cld             ;df=0

        

См. также: урок 11 и команды stc, movs/movsb/movsw/movsd,

cmps/cmpsb/cmpsw/cmpsd, scas/scasb/scasw/scasd,

lods/lodsb/lodsw/lodsd, stos/stosb/stosw/stosd,

ins/insb/insw/insd, outs

CLI

(CLear Interrupt flag)

Сброс флага прерывания

 

Схема команды: 

cli 

Назначение: сброс флага прерывания if.

Синтаксис 

Алгоритм работы: 

установка флага if в ноль.

Состояние флагов после выполнения команды: 

09

IF

0

Применение: 

Данная команда используется для сброса флага if в ноль. Такая необходимость может возникнуть при разработке программ обработки прерываний.

        cli             ;if=0

        

См. также: урок 15 и команды int, iret/iretd, sti

CMC

(CoMplement Carry flag)

Инвертирование флага переноса

 

Схема команды: 

cmc 

Назначение: изменение значения флага переноса cf на обратное.

Синтаксис 

Алгоритм работы: 

инвертирование значения флага переноса cf.

Состояние флагов после выполнения команды: 

00

CF

r

Применение: 

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

proc1   proc

...

        cmc

...

proc1   endp

...

        call    proc1

        jc      m1      ;если cf=1, то переход на m1

...

m1:

...

        

См. также: уроки 8, 9, 15 и команды clc, stc, jc, jnc

CMP

(CoMPare operands)

Сравнение операндов

 

Схема команды: 

cmp операнд1,операнд2 

Назначение: сравнение двух операндов.

Синтаксис 

Алгоритм работы: 

  •  выполнить вычитание (операнд1-операнд2);
  •  в зависимости от результата установить флаги, операнд1 и операнд2 не изменять (то есть результат не запоминать).

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

r

r

Применение: 

Данная команда используется для сравнения двух операндов методом вычитания, при этом операнды не изменяются. По результатам выполнения команды устанавливаются флаги. Команда cmp применяется с командами условного перехода и командой установки байта по значению setcc.

len     equ     10

...

        cmp     ax,len

        jne     m1      ;переход если (ax)<>len

        jmp     m2      ;переход если (ax)=len

        

См. также: уроки 10, 11, 12 и команды cmps/cmpsb/cmpsw/cmpsd, cmpxchg, sub, jcc, setcc

CMPS/CMPSB/CMPSW/CMPSD

(CoMPare String Byte/Word/Double word operands)

Сравнение строк байтов/слов/двойных слов

 

Схема команды: 

cmps приемник,источник 

cmpsb 

cmpsw 

cmpsd

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

Синтаксис 

Алгоритм работы: 

  •  выполнить вычитание элементов (источник - приемник), адреса элементов предварительно должны быть загружены:
    •  адрес источника — в пару регистров ds:esi/si;
    •  адрес назначения — в пару регистров es:edi/di;
  •  в зависимости от состояния флага df изменить значение регистров esi/si и edi/di:
    •  если df=0, то увеличить содержимое этих регистров на длину элемента последовательности;
    •  если df=1, то уменьшить содержимое этих регистров на длину элемента последовательности;
  •  в зависимости от результата вычитания установить флаги:
    •  если очередные элементы цепочек не равны, то cf=1, zf=0;
    •  если очередные элементы цепочек или цепочки в целом равны, то cf=0, zf=1;
  •  при наличии префикса выполнить определяемые им действия (см. команды repe/repne).

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

r

r

Применение: 

Команды без префиксов осуществляют простое сравнение двух элементов в памяти. Размеры сравниваемых элементов зависят от применяемой команды. Команда cmps может работать с элементами размером в байт, слово, двойное слово. В качестве операндов в команде указываются идентификаторы последовательностей этих элементов в памяти. Реально эти идентификаторы используются лишь для получения типов элементов последовательностей, а их адреса должны быть предварительно загружены в указанные выше пары регистров. Транслятор, обработав команду cmps и выяснив тип операндов, генерирует одну из машинных команд cmpsb, cmpsw или cmpsd. Машинного аналога для команды cmps нет. Для адресации назначения обязательно должен использоваться регистр es, а для адресации источника можно делать замену сегмента с использованием соответствующего префикса.

Для того чтобы эти команды можно было использовать для сравнения последовательности элементов, имеющих размерность байт, слово, двойное слово, необходимо использовать один из префиксов repe или repne. Префикс repe заставляет циклически выполняться команды сравнения до тех пор, пока содержимое регистра ecx/cx не станет равным нулю или пока не совпадут очередные сравниваемые элементы цепочек (флаг zf=1). Префикс repne заставляет циклически производить сравнение до тех пор, пока не будет достигнут конец цепочки (ecx/cx=0) либо не встретятся различающиеся элементы цепочек (флаг zf=0).

.data

obl1    db      'Строка для сравнения'

obl1    db      'Строка для сравнения'

a_obl1  dd      obl1

a_obl2  dd      obl2

.code

...

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

        mov     cx,20   ;длина цепочки

        lds     si,a_obl1       ;адрес источника в пару ds:si

        les     di,a_obl2       ;адрес назначения в пару ds:si

repe    cmpsb           ;сравнивать, пока равны

        jnz     m1      ;если не конец цепочки, то встретились разные элементы

...                     ;действия, если цепочки совпали

...

m1:

...                     ;действия, если цепочки не совпали

        

См. также: уроки 10, 11 и команды ins, lods, movs, outs, scas, stos, repe, repz, repne, repnz 

CMPXCHG

(CoMPare and eXCHanGe)

Сравнение и обмен

 

Схема команды: 

cmpxchg приемник,источник(аккумулятор) 

Назначение: сравнение и обмен значений между источником и приемником.

Синтаксис 

Алгоритм работы: 

  •  выполнить сравнение элементов источник и приемник;
  •  если источник и приемник не равны, то:
    •  установить zf=0;
    •  переслать содержимое операнда приемник в источник (регистр al/ax/eax).
  •  если источник и приемник равны, то:
    •  установить zf=1;
    •  переслать содержимое операнда источник (регистр al/ax/eax) по месту операнда приемник.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

r

r

Применение: 

Команды сравнивают два операнда. Один из сравниваемых операндов находится в аккумуляторе (регистре al/ax/eax), другой может находиться в памяти или регистре общего назначения. Если значения равны, то производится замена содержимого операнда приемник содержимым источника, находящимся в регистре-аккумуляторе. Если значения не равны, то производится замена содержимого операнда источника находящимся в регистре-аккумуляторе содержимым операнда назначения. Определить тот факт, была ли произведена смена значения в аккумуляторе (то есть были ли не равны сравниваемые операнды), можно по значению флага zf.

.486

        mov     ax,114eh

        mov     bx,8e70h

        cmpxchg bx,ax

        jz      m1      ;переход, если zf=1, то есть операнды равны

                        ;и ax не изменился

...                     ;действия, если операнды не равны

m1:

        

См. также: уроки 7, 10 и команды cmp, xchg 

CWD

(Convert Word to Double word)

Преобразование слова в двойное слово

 

Схема команды: 

cwd 

Назначение: расширение слова со знаком до размера двойного слова со знаком.

Синтаксис 

Алгоритм работы: 

копирование значения старшего бита регистра ax во все биты регистра dx. Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

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

        mov     ax,25

...

        mov     bx,4

        cwd

        div     bx

        

См. также: урок 8 и команды cbw, cdq, cwde, div, idiv, mul, imul, add, adc, sub, sbb

CDQ

(Convert Double word to Quad word)

Преобразование двойного слова в учетверенное слово

 

Схема команды: 

cdq 

Назначение: расширение двойного слова со знаком до размера учетверенного слова (64 бита) со знаком.

Синтаксис 

Алгоритм работы: 

копирование значения старшего бита регистра eax на все биты регистра edx. Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команду cdq можно использовать для распространения значения знакового бита в регистре eax на все биты регистра edx. Данную операцию, в частности, можно использовать для подготовки к операции деления, для которой размер делимого должен быть в два раза больше размера делителя.

.386

delimoe dd      ...

delitel dd      ...

...

        mov     eax,delimoe

        cdq

        idiv    delitel ;частное в eax, остаток в edx

        

См. также: урок 8 и команды cbw, cwd, cwde, div, idiv

DAA

(Decimal Adjust for Addition)

Десятичная коррекция после сложения

 

Схема команды: 

daa 

Назначение: коррекция упакованного результата сложения двух BCD-чисел в упакованном формате.

Синтаксис 

Алгоритм работы: 

команда работает только с регистром al и анализирует наличие следующих ситуаций:

  •  Ситуация 1. В результате предыдущей команды сложения флаг af=1 или значение младшей тетрады регистра al>9. Напомним, что флаг af устанавливается в 1 в случае переноса двоичной единицы из бита 3 младшей тетрады в старшую тетраду регистра al (если значение превысило 0fh). Наличие одного из этих двух признаков говорит о том, что значение младшей тетрады превысило 9h.
  •  Ситуация 2. В результате предыдущей команды сложения флаг сf=1 или значение регистра al>9fh. Напомним, что флаг cf устанавливается в 1 в случае переноса двоичной единицы в старший бит операнда (если значение превысило 0ffh в случае регистра al). Наличие одного из этих двух признаков говорит о том, что значение в регистре al превысило 9fh.

Если имеет место одна из этих двух ситуаций, то регистр al корректируется следующим образом:

  •  для ситуации 1 содержимое регистра al увеличивается на 6;
  •  для ситуации 2 содержимое регистра al увеличивается на 60h;
  •  если имеют место обе ситуации, то корректировка начинается с младшей тетрады.

Состояние флагов после выполнения команды (в случае, если были переносы): 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

1

1

Состояние флагов после выполнения команды (в случае, если переносов не было): 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

0

0

Применение: 

Эту команду следует применять после сложения двух упакованных BCD-чисел с целью корректировки получающегося двоичного результата сложения в правильное двузначное десятичное число. После команды daa следует анализировать состояние флага cf. Если он равен 1, то это говорит о том, что был перенос единицы в старший разряд и это нужно учесть для сложения старших десятичных цифр BCD-числа.

        mov     al,69h  ;69h — упакованное BCD-число

        mov     bl,74h  ;74h — упакованное BCD-число

        adc     al,bl   ;al=0ddh

        daa             ;cf=1, al=43h

;если перенос, то переход на ту ветвь программы,

;где он будет учтен:

        jc m1

        

См. также: урок 8, Приложение 7 и команды aaa, aad, aam, aas, das

DAS

(Decimal Adjust for Subtraction)

Десятичная коррекция после вычитания

 

Схема команды: 

das 

Назначение: коррекция упакованного результата вычитания двух BCD-чисел в упакованном формате.

Синтаксис 

Алгоритм работы: 

команда das работает только с регистром al и анализирует наличие следующих ситуаций:

  •  Ситуация 1. В результате предыдущей команды сложения флаг af =1 или значение младшей тетрады регистра al>9. Напомним, что для случая вычитания флаг af устанавливается в 1 в случае заема двоичной единицы из старшей тетрады в младшую тетраду регистра al. Наличие одного из этих двух признаков говорит о том, что значение младшей тетрады превысило 9h и его нужно корректировать.
  •  Ситуация 2. В результате предыдущей команды сложения флаг сf =1 или значение регистра al>9fh. Напомним, что для случая вычитания флаг cf устанавливается в 1 в случае заема двоичной единицы. Наличие одного из этих двух признаков говорит о том, что значение в регистре al превысило 9fh.

Если имеет место одна из этих ситуаций, то регистр al корректируется следующим образом:

  •  для ситуации 1 содержимое регистра al уменьшается на 6;
  •  для ситуации 2 содержимое регистра al уменьшается на 60h;
  •  если имеют место обе ситуации, то корректировка начинается с младшей тетрады.

Состояние флагов после выполнения команды (в случае, если были переносы): 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

1

1

Состояние флагов после выполнения команды (в случае, если переносов не было): 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

0

0

Применение: 

Команду das следует применять после вычитания двух упакованных BCD-чисел с целью корректировки получающегося двоичного результата вычитания в правильное двузначное десятичное число. После команды das следует анализировать состояние флага cf. Если он равен 1, то это говорит о том, что был заем единицы в старший разряд и это нужно учесть в дальнейших действиях. Если у вычитаемого нет больше старших разрядов, то результат следует трактовать как отрицательное двоичное дополнение. Для определения его абсолютного значения нужно вычесть 100 из результата в al. Если у вычитаемого еще есть старшие разряды, то факт заема нужно просто учесть уменьшением младшего из этих оставшихся старших разрядов на единицу.

        mov     ah,08h  ;ah=08h

        mov     al,05h  ;al=05h

        add     al,ah   ;al=al+ah=05h+08h=0dh — не BCD-число

        xor     ah,ah   ;ah=0

        aaa             ;ah=01h,al=03h — результат скорректирован

        

См. также: урок 8, Приложение 7 и команды aaa, aad, aam, aas, daa

DEC

(DECrement operand by 1)

Уменьшение операнда на единицу

 

Схема команды: 

dec операнд 

Назначение: уменьшение значения операнда в памяти или регистре на 1.

Синтаксис 

Алгоритм работы: 

команда вычитает 1 из операнда. Состояние флагов после выполнения команды: 

11

07

06

04

02

OF

SF

ZF

AF

PF

r

r

r

r

Применение: 

Команда dec используется для уменьшения значения байта, слова, двойного слова в памяти или регистре на единицу. При этом заметьте то, что команда не воздействует на флаг cf.

        mov     al,9

...

        dec     al      ;al=8

        

См. также: урок 8 и команды inc, sub 

DIV

(DIVide unsigned)

Деление беззнаковое

 

Схема команды: 

div делитель 

Назначение: выполнение операции деления двух двоичных беззнаковых значений.

Синтаксис 

Алгоритм работы: 

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

  •  если делитель размером в байт, то делимое должно быть расположено в регистре ax. После операции частное помещается в al, а остаток — в ah;
  •  если делитель размером в слово, то делимое должно быть расположено в паре регистров dx:ax, причем младшая часть делимого находится в ax. После операции частное помещается в ax, а остаток — в dx;
  •  если делитель размером в двойное слово, то делимое должно быть расположено в паре регистров edx:eax, причем младшая часть делимого находится в eax. После операции частное помещается в eax, а остаток — в edx.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

?

?

?

?

?

Применение: 

Команда выполняет целочисленное деление операндов с выдачей результата деления в виде частного и остатка от деления. При выполнении операции деления возможно возникновение исключительной ситуации: 0 — ошибка деления. Эта ситуация возникает в одном из двух случаев: делитель равен 0 или частное слишком велико для его размещения в регистре eax/ax/al.

        mov     ax,10234

        mov     bl,154

        div     bl      ;ah=остаток, al=частное

        

См. также: урок 8, приложение 7 и команду idiv

ENTER

(setup parameter block for ENTERing procedure)

Установка кадра стека для параметров процедуры

 

Схема команды: 

enter loc_size,lex_lev 

Назначение: установка границы в стеке для локальных переменных процедуры.

Синтаксис 

Алгоритм работы: 

  •  поместить текущее значение регистра ebp/bp в стек;
  •  сохранить текущее значение esp/sp в промежуточной переменной fp (имя переменной выбрано случайно);
  •  если лексический уровень вложенности (операнд lex_lev) не равен нулю, то (lex_lev–1) раз делать следующее:
    •  в зависимости от установленного режима адресации use16 или use32 выполнить вычитание (bp–2) или (ebp–4) и записать результат обратно в ebp/bp;
    •  сохранить значение ebp/bp в стеке;
    •  сохранить в стеке значение промежуточной переменной fp;
  •  записать значение промежуточной переменной fp в регистр ebp/bp;
  •  уменьшить значение регистра esp/sp на величину, заданную первым операндом, минус размер области локальных переменных loc_size: esp/sp=(esp/sp)–loc_size.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команда enter специально введена в систему команд микропроцессора для поддержки блочно-структурированных языков высокого уровня типа Pascal или С. В этих языках программа разбивается на блоки. В блоках можно описать свои собственные (локальные) идентификаторы, которые не могут быть использованы вне этого блока. К примеру, на рисунке ниже в виде блоков изображена структура некоторой программы.

Изображение структуры некоторой программы в виде блоков

В правом верхнем углу каждого блока (процедуры) стоит номер лексического уровня вложенности этого блока относительно других блоков программы. Большинство блочно-структурированных языков в качестве основного метода распределения памяти для переменных в блоках используют автоматическое распределение памяти. Это означает, что при входе в блок (вызове процедуры и т. п.) в некотором месте памяти (или в стеке) выделяется область памяти для переменных этого блока (ее можно назвать областью инициализации). После выхода из этого блока связь программы с этой областью теряется, то есть эти переменные становятся недоступными. Но если, как в нашем примере, в этой процедуре есть вложенные блоки (процедуры), то для некоторого внутреннего блока (например, C) могут быть доступны области инициализации (переменные) блоков, объемлющих данный блок. В нашем примере для блока C доступны также переменные блоков B и A, но не D. Возникает вопрос: как же программа, находясь в конкретной точке своего выполнения, может отслеживать то, какие области инициализации ей доступны? Это делается с помощью структуры данных, называемой дисплеем. Дисплей содержит указатели на самую последнюю область текущего блока и на области инициализации всех блоков, объемлющих данный блок в программе. Например, если в программе A была вызвана сначала процедура B, а затем C, то дисплей содержит указатели на области инициализации A, B и C (см. рисунок ниже).

Если после этого вызвать процедуру D (в то время как B и C еще не завершены), то картина изменится.

После того как некоторый блок (процедура) завершает свою работу, ее область инициализации удаляется из памяти (стека) и одновременно соответствующим образом корректируется дисплей. Большинство языков высокого уровня хранят локальные данные блоков в стеке. Эти переменные называют еще автоматическими или динамическими. Память для них резервируется путем уменьшения значения регистра-указателя стека esp/sp на величину, равную длине области, занимаемой этими динамическими переменными. Доступ к этим переменным осуществляется посредством регистра ebp/bp. Если один блок вложен в другой, то для его динамических (локальных) переменных также выделяется место (кадр) в стеке, но в этот кадр помещается указатель на кадр стека для включающего его блока. Команды enter и leave как раз и позволяют поддержать в языке ассемблера принципы работы с переменными блоков как в блочно-структурированных языках. Дисплей организуется с помощью второго операнда команды enter и стека. Например, в начале работы главной процедуры A и после вызова процедуры B кадр стека будет выглядеть так.

Соответственно, после вызова процедур C и D стек будет выглядеть, как показано ниже.

Таким образом, видно, что используя дисплей, мы фактически имеем адреса областей инициализации, доступных по признаку вложенности объемлющих блоков. Обратный процесс завершения работы с блоками и удаления соответствующих областей инициализации поддерживается командой leave.

.286

proc1   proc

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

;proc1 16 байт

;лексический уровень вложенности 0

        enter   16,0

...

        leave

        ret

proc1   endp

        

См. также: урок 14 и команды leave, ret 

HLT

(HaLT)

Остановка

 

Схема команды: 

hlt 

Назначение: остановка микропроцессора до прерывания или перезагрузки.

Синтаксис 

Алгоритм работы: 

перевод микропроцессора в состояние остановки.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

В результате выполнения команды микропроцессор переходит в состояние остановки. Из этого состояния его можно вывести сигналами на входах RESET, NMI, INTR. Если для возобновления работы микропроцессора используется прерывание, то сохраненное значение пары cs:eip/ip указывает на команду, следующую за hlt. Для иллюстрации применения данной команды рассмотрим еще один способ переключения микропроцессора из защищенного в реальный режим и его возврата обратно в реальный режим (см. урок 16). Как известно, в микропроцессоре не предусмотрено специальных средств для подобного переключения. Сброс микропроцессора можно инициировать, если вывести байт со значением 0feh в порт клавиатуры 64h. После этого микропроцесор переходит в реальный режим и управление получает программа BIOS, которая анализирует байт отключения в CMOS-памяти по адресу 0fh. Для нас интерес представляют два значения этого байта — 5h и 0ah:

  •  5h — сброс микропроцессора инициирует инициализацию программируемого контроллера прерываний на значение базового вектора 08h (см. уроки 15 и 17). Далее управление передается по адресу, который находится в ячейке области данных BIOS 0040:0067;
  •  0ah — сброс микропроцессора инициирует непосредственно передачу управления по адресу в ячейке области данных BIOS 0040:0067 (то есть без перепрограммирования контроллера прерываний).

Таким образом, если вы не используете прерываний, то достаточно установить байт 0fh в CMOS-памяти в 0ah. Предварительно, конечно, вы должны инициализировать ячейку области данных BIOS 0040:0067 значением адреса, по которому необходимо передать управление после сброса. Для программирования CMOS-памяти используются номера портов 070h и 071h. Вначале в порт 070h заносится нужный номер ячейки CMOS-памяти, а затем в порт 071h — новое значение этой ячейки.

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

;в защищенный режим:

        push    es

        mov     ax,40h

        mov     es,ax

        mov     word ptr es:[67h],offset ret_real

;ret_real — метка в программе, с которой должно

;начаться выполнение программы после сброса

        mov     es:[69h],cs

        mov     al,0fh  ;будем обращаться к ячейке 0fh в CMOS

        out     70h,al

        jmp     $+2     ;чуть задержимся, чтобы аппаратура отработала

;сброс без перепрограммирования контроллера

        mov     al,0ah

        out     71h,al

;переходим в защищенный режим установкой

;бита 0 cr0 в 1 (см. урок 16)

;работаем в защищенном режиме

;готовимся перейти обратно в реальный режим

        mov     al,01fch

        out     64h,al  ;сброс микропроцессора  hlt

;остановка до физического окончания процесса сброса

        ret_real:       ...     ;метка, на которую будет передано

                        ;управление после сброса

        

См. также: уроки 15, 16, 17

IDIV

(Integer DIVide)

Деление целочисленное со знаком

 

Схема команды: 

idiv делитель 

Назначение: операция деления двух двоичных значений со знаком.

Синтаксис 

Алгоритм работы: 

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

  •  если делитель размером в байт, то делимое должно быть расположено в регистре ax. После операции частное помещается в al, а остаток — в ah;
  •  если делитель размером в слово, то делимое должно быть расположено в паре регистров dx:ax, причем младшая часть делимого находится в ax. После операции частное помещается в ax, а остаток — в dx;
  •  если делитель размером в двойное слово, то делимое должно быть расположено в паре регистров edx:eax, причем младшая часть делимого находится в eax. После операции частное помещается в eax, а остаток — в edx;

Остаток всегда имеет знак делимого. Знак частного зависит от состояния знаковых битов (старших разрядов) делимого и делителя.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

?

?

?

?

?

Применение: 

Команда выполняет целочисленное деление операндов с учетом их знаковых разрядов. Результатом деления являются частное и остаток от деления. При выполнении операции деления возможно возникновение исключительной ситуации: 0 — ошибка деления. Эта ситуация возникает в одном из двух случаев: делитель равен 0 или частное слишком велико для его размещения в регистре eax/ax/al.

;деление слов

        mov     ax,1045 ;делимое

        mov     bx,587  ;делитель

        cwd             ;расширение делимого dx:ax

        idiv    bx      ;частное в ax, остаток в dx

        

См. также: урок 8, приложение 7 и команду div

IMUL

(Integer MULtiply)

Умножение целочисленное со знаком

 

Схема команды: 

imul множитель_1 

imul множ_1,множ_2 

imul рез-т,множ_1,множ_2

Назначение: операция умножения двух целочисленных двоичных значений со знаком.

Синтаксис 

Алгоритм работы: 

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

  •  если операнд, указанный в команде, — байт, то второй сомножитель располагается в al;
  •  если операнд, указанный в команде, — слово, то второй сомножитель располагается в ax;
  •  если операнд, указанный в команде, — двойное слово, то второй сомножитель располагается в eax.

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

  •  при умножении байтов результат помещается в ax;
  •  при умножении слов результат помещается в пару dx:ax;
  •  при умножении двойных слов результат помещается в пару edx:eax.

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

  •  в команде с двумя операндами первый операнд определяет местоположение первого сомножителя. На его место впоследствии будет записан результат. Второй операнд определяет местоположение второго сомножителя;
  •  в команде с тремя операндами первый операнд определяет местоположение результата, второй операнд — местоположение первого сомножителя, третий операнд может быть непосредственно заданным значением размером в байт, слово или двойное слово.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

?

?

?

r

Команда imul устанавливает в ноль флаги of и cf, если размер результата соответствует регистру назначения. Если эти флаги отличны от нуля, то это означает, что результат слишком велик для отведенных ему регистром назначения рамок и необходимо указать больший по размеру регистр для успешного завершения данной операции умножения. Конкретными условиями сброса флагов of и cf в ноль являются следующие условия:

  •  для однооперандной формы команды imul регистры ax/dx/edx являются знаковыми расширениями регистров al/ax/eax;
  •  для двухоперандной формы команды imul для размещения результата умножения достаточно размерности указанных регистров назначения r16/r32;
  •  то же для трехоперандной команды умножения.

Применение: 

Команда выполняет целочисленное умножение операндов с учетом их знаковых разрядов. Для выполнения этой операции необходимо наличие двух сомножителей. Размещение и задание их местоположения в команде зависит от формы применяемой команды умножения, которая, в свою очередь, определяется моделью микропроцессора. Так, для микропроцессора i8086 возможна только однооперандная форма команды, для последующих моделей микропроцессоров дополнительно можно использовать двух- и трехоперандные формы этой команды.

.486

...

        mov     bx,186

        imul    eax,bx,8

;если результату не хватило размерности операнда1,

;то перейдем на m1, где скорректируем ситуацию:

        jc      m1

        

См. также: урок 8, приложение 7 и команду mul

IN

(INput operand from port)

Ввод операнда из порта

 

Схема команды: 

in аккумулятор,ном_порта 

Назначение: ввод значения из порта ввода-вывода.

Синтаксис 

Алгоритм работы: 

Передает байт, слово, двойное слово из порта ввода-вывода в один из регистров al/ax/eax. Состояние флагов после выполнения команды: выполнение команды не влияет на флаги.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команда применяется для прямого управления оборудованием компьютера посредством портов. Номер порта задается вторым операндом в виде непосредственного значения или значения в регистре dx. Непосредственным значением можно задать порт с номером в диапазоне 0-255. При использовании порта с большим номером используется регистр dx. Размер данных определяется размерностью первого операнда и может быть байтом, словом, двойным словом. В качестве примера применения рассмотрим фрагмент обработчика прерывания от клавиатуры 9. Это прерывание вызывается всякий раз при нажатии любой клавиши на клавиатуре. Обработчик этого прерывания должен прочитать скан-код клавиши, подтвердить микропроцессору клавиатуры факт приема скан-кода, преобразовать этот код в соответствии с клавишами-переключателями и поместить преобразованный код в буфер клавиатуры, находящийся в области BIOS. Действия чтения и подтверждения приема скан-кода могут выглядеть, к примеру, так:

        in      al,60h  ;читаем скан-код

        push    ax      ;сохраним его на время

        in      al,61h  ;читаем порт 61h

        or      al,80h  ;старший бит байта из порта 61h в 1

        out     61h,al  ;подтверждаем факт приема скан-кода

        pop     ax

        out     61h,al  ;восстановили байт в порту 61h

        

См. также: урок 7 и команды out, ins/insb/insw/insd, outs

INC

(INCrement operand by 1)

Увеличить операнд на 1

 

Схема команды: 

inc операнд 

Назначение: увеличение значения операнда в памяти или регистре на 1.

Синтаксис 

Алгоритм работы: 

команда увеличивает операнд на единицу.

Состояние флагов после выполнения команды: 

11

07

06

04

02

OF

SF

ZF

AF

PF

r

r

r

r

Применение: 

Команда используется для увеличения значения байта, слова, двойного слова в памяти или регистре на единицу. При этом команда не воздействует на флаг cf.

        inc     ax      ;увеличить значение в ax на 1

        

См. также: урок 8 и команды dec, add, adc

INS/INSB/INSW/INSD

(Input String Byte/Word/Double word operands)

Ввод строк байтов/слов/двойных слов из порта

 

Схема команды: 

ins приемник,порт 

insb 

insw 

insd

Назначение: ввод из порта в память последовательности байт, слов, двойных слов.

Синтаксис 

Алгоритм работы: 

  •  передать данные из порта ввода-вывода, номер которого загружен в регистр dx, в память по адресу es:edi/di;
  •  в зависимости от состояния флага df изменить значение регистров edi/di:
    •  если df=0, то увеличить содержимое этих регистров на длину структурного элемента последовательности;
    •  если df=1, то уменьшить содержимое этих регистров на длину структурного элемента последовательности;
  •  при наличии префикса выполнить определяемые им действия (см. команду rep).

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команда вводит данные из порта ввода-вывода, номер которого загружен в регистр dx, в память по адресу es:edi/di. Сегментная составляющая адреса должна быть обязательно в регистре es. Замена сегментного регистра недопустима. Непосредственное задание порта в команде также недопустимо - для этого используется регистр dx. Размеры вводимых элементов зависят от применяемой команды. Команда ins может работать с элементами размером в байт, слово, двойное слово. В качестве операндов в команде указывается символическое имя ячейки памяти, в которую вводятся элементы из порта ввода-вывода. Реально это символическое имя используется лишь для получения типа элемента последовательности, а его адрес должен быть предварительно загружен в пару регистров es:edi/di. Транслятор, обработав команду ins и выяснив тип операнда, генерирует одну из машинных команд insb, insw или insd. Машинного аналога для команды ins нет. Для того чтобы эти команды можно было использовать для ввода последовательности элементов, имеющих размерность байт, слово, двойное слово, необходимо использовать префикс rep. Префикс rep заставляет циклически выполняться команду ввода до тех пор, пока содержимое регистра ecx/cx не станет равным нулю.

.286

;ввести 10 байт из порта 300h (номер порта bgr условно)

;в цепочку байт в памяти по адресу

str_10  db      10 dup(0)

adr_str dd      str_10

        les     di,adr_str

        mov     dx,300h

rep     insb

...

        

См. также: уроки 2, 11 и команды cmps/cmpsb/cmpsw/cmpsd, lods/lodsb/lodsw/lodsd, movs/movsb/movsw/movsd, outs, scas/scasb/scasw/scasd, stos/stosb/stosw/stosd, rep/repe/repz/repne/repnz

INT

(INTerrupt)

Вызов подпрограммы обслуживания прерывания

 

Схема команды: 

int номер_прерывания 

Назначение: вызов подпрограммы обслуживания прерывания с номером прерывания, заданным операндом команды.

Синтаксис 

Алгоритм работы: 

  •  записать в стек регистр флагов eflags/flags и адрес возврата. При записи адреса возврата вначале записывается содержимое сегментного регистра cs, затем содержимое указателя команд eip/ip;
  •  сбросить в ноль флаги if и tf;
  •  передать управление на программу обработки прерывания с указанным номером. Механизм передачи управления зависит от режима работы микропроцессора (см. уроки 15 и 17).

Состояние флагов после выполнения команды: 

09

08

IF

TF

0

Применение: 

Как видно из синтаксиса, существуют две формы этой команды:

  •  int 3 — имеет свой индивидуальный код операции 0cch и занимает один байт. Это обстоятельство делает ее очень удобной для использования в различных программных отладчиках для установки точек прерывания путем подмены первого байта любой команды. Микропроцессор, встречая в последовательности команд команду с кодом операции 0cch, вызывает программу обработки прерывания с номером вектора 3, которая служит для связи с программным отладчиком.
  •  Вторая форма команды занимает два байта, имеет код операции 0cdh и позволяет инициировать вызов подпрограммы обработки прерывания с номером вектора в диапазоне 0–255. Особенности передачи управления, как было отмечено, зависят от режима работы микропроцессора.

 

;вызов обработчика аппаратного прерывания 08h из программы:

        int     08h

        

См. также: уроки 15, 17 и команды into, iret/iretd

INTO

(INTerrupt if Overflow)

Прерывание, если переполнение

 

Схема команды: 

into 

Назначение: инициирование прерывания с номером 4, если установлен флаг of.

Синтаксис 

Алгоритм работы: 

Проанализировать состояние флага of:

  •  если of=0, то никаких действий производить не нужно — передать управление на следующую команду;
  •  если of=1, то дальнейшие действия, как при команде int, то есть:
    •  записать в стек регистр флагов eflags/flags и адрес возврата. При записи адреса возврата вначале записывается содержимое сегментного регистра cs, затем содержимое указателя команд eip/ip;
    •  сбросить в ноль флаги if и tf;
    •  передать управление на программу обработки прерывания с данным номером. Механизм передачи зависит от режима работы микропроцессора (см. уроки 15 и 17).

Состояние флагов после выполнения команды: 

09

08

IF

TF

r

r

Применение: 

Свойство этой команды инициировать вызов подпрограммы обработки прерывания с номером вектора 4 определяет варианты ее применения. Если предыдущая команда в программе может в результате своей работы установить флаг переполнения of (к примеру, арифметические команды), то для обнаружения и обработки такой ситуации можно использовать команду into. Особенности передачи управления и обработки (корректировки) результата зависят от режима работы микропроцессора.

.486

...

        mov     bx,186

        imul    eax,bx,8

;если результату не хватило размерности операнда1,

;то of установится в 1

;исправим ситуацию в обработчике прерывания 3

        into

        

См. также: уроки 8, 15, 17 и команды int, iret/iretd, imul

IRET/IRETD

(Interrupt RETurn)

Возврат из прерывания

 

Схема команды: 

iret 

iretd 

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

Синтаксис 

Алгоритм работы: 

Работа команды зависит от режима работы микропроцесссора:

  •  в реальном режиме команда iret последовательно извлекает из стека и затем восстанавливает в микропроцессоре содержимое следующих регистров: eip/ip, cs, eflags/flags. Далее прерванная программа продолжается с точки прерывания;
  •  в защищенном режиме действия команды зависят от состояния флага NT (вложенной задачи) в регистре флагов:
    •  если NT=0, то производятся действия по возврату управления прерванной программе, при этом характер этих действий зависит от соотношения уровней привилегированности прерванной программы и программы обработки прерывания;
    •  в случае NT=1 производятся действия по переключению задач.

Состояние флагов после выполнения команды: 

11

10

09

08

07

06

04

02

00

OF

DF

IF

TF

SF

ZF

AF

PF

CF

r

r

r

r

r

r

r

r

r

Применение: 

Команду iret необходимо применять для восстановления сохраненных командой int регистров флагов, указателя команд и сегментного регистра кода. Число этих команд в программе обработки прерывания должно соответствовать количеству точек выхода из нее. Команда iretd используется в старших моделях микропроцессоров для извлечения из стека и восстановления 32-битных регистров.

my_int1c        proc

;программа обработки прерывания 1Ch

...

        iret

        endp

        

См. также: уроки 15, 17 и команды int, into

JCC

JCXZ/JECXZ

(Jump if condition)

(Jump if CX=Zero/ Jump if ECX=Zero)

Переход, если выполнено условие

Переход, если CX/ECX равен нулю

 

Схема команды: 

jcc метка 

jcxz метка 

jecxz метка

Назначение: переход внутри текущего сегмента команд в зависимости от некоторого условия.

Синтаксис 

Алгоритм работы команд (кроме jcxz/jecxz): 

Проверка состояния флагов в зависимости от кода операции (оно отражает проверяемое условие):

  •  если проверяемое условие истинно, то перейти к ячейке, обозначенной операндом;
  •  если проверяемое условие ложно, то передать управление следующей команде.

Алгоритм работы команды jcxz/jecxz: 

Проверка условия равенства нулю содержимого регистра ecx/cx:

  •  если проверяемое условие истинно, то есть содержимое ecx/cx равно 0, то перейти к ячейке, обозначенной операндом метка;
  •  если проверяемое условие ложно, то есть содержимое ecx/cx не равно 0, то передать управление следующей за jcxz/jecxz команде программы.

Состояние флагов после выполнения команды: 

11

07

06

05

04

03

02

01

00

OF

SF

ZF

0

AF

0

PF

1

CF

?

 

 

?

 

r

Применение (кроме jcxz/jecxz): 

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

Команда

Состояние проверяемых флагов

Условие перехода

JA

CF = 0 и ZF = 0

если выше

JAE

CF = 0

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

JB

CF = 1

если ниже

JBE

CF = 1 или ZF = 1

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

JC

CF = 1

если перенос

JE

ZF = 1

если равно

JZ

ZF = 1

если 0

JG

ZF = 0 и SF = OF

если больше

JGE

SF = OF

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

JL

SF <> OF

если меньше

JLE

ZF=1 или SF <> OF

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

JNA

CF = 1 и ZF = 1

если не выше

JNAE

CF = 1

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

JNB

CF = 0

если не ниже

JNBE

CF=0 и ZF=0

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

JNC

CF = 0

если нет переноса

JNE

ZF = 0

если не равно

JNG

ZF = 1 или SF <> OF

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

JNGE

SF <> OF

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

JNL

SF = OF

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

JNLE

ZF=0 и SF=OF

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

JNO

OF=0

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

JNP

PF = 0

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

JNS

SF = 0

если знак плюс (знаковый (старший) бит результата равен 0)

JNZ

ZF = 0

если нет нуля

JO

OF = 1

если переполнение

JP

PF = 1

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

JPE

PF = 1

то же, что и JP, то есть четный паритет

JPO

PF = 0

то же, что и JNP

JS

SF = 1

если знак минус (знаковый (старший) бит результата равен 1)

JZ

ZF = 1

если ноль

Логические условия "больше" и "меньше" относятся к сравнениям целочисленных значений со знаком, а "выше и "ниже" — к сравнениям целочисленных значений без знака. Если внимательно посмотреть, то у многих команд можно заметить одинаковые значения флагов для перехода. Это объясняется наличием нескольких ситуаций, которые могут вызвать одинаковое состояние флагов. В этом случае с целью удобства ассемблер допускает несколько различных мнемонических обозначений одной и той же машинной команды условного перехода. Эти команды ассемблера по действию абсолютно равнозначны, так как это одна и та же машинная команда. Изначально в микропроцессоре i8086 команды условного перехода могли осуществлять только короткие переходы в пределах -128...+127 байт, считая от следующей команды. Начиная с микропроцессора i386, эти команды уже могли выполнять любые переходы в пределах текущего сегмента команд. Это стало возможным за счет введения в систему команд микропроцессора дополнительных машинных команд. Для реализации межсегментных переходов необходимо комбинировать команды условного перехода и команду безусловного перехода jmp. При этом можно воспользоваться тем, что практически все команды условного перехода парные, то есть имеют команды, проверяющие обратные условия.

Применение jcxz/jecxz: 

Команда

Состояние флагов в eflags/flags

Условие перехода

JCXZ

не влияет

если регистр CX=0

JECXZ

не влияет

если регистр ECX=0

Команду jcxz/jecxz удобно использовать со всеми командами, использующими регистр ecx/cx для своей работы. Это команды организации цикла и цепочечные команды. Очень важно отметить то, что команда jcxz/jecxz, в отличие от других команд перехода, может выполнять только близкие переходы в пределах -128...+127 байт, считая от следующей команды. Поэтому для нее особенно актуальна проблема передачи управления далее чем в указанном диапазоне. Для этого можно привлечь команду безусловного перехода jmp. Например, команду jcxz/jecxz можно использовать для предварительной проверки счетчика цикла в регистре cx для обхода цикла, если его счетчик нулевой.

...

        jcxz    m1      ;обойти цикл, если cx=0

cycl:

;некоторый цикл

        loop    cycl

m1:     ...

        

См. также: уроки 10, 11 и команду jmp 

JMP

(JuMP)

Переход безусловный

 

Схема команды: 

jmp метка 

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

Синтаксис 

Алгоритм работы: 

Команда jmp в зависимости от типа своего операнда изменяет содержимое либо только одного регистра eip, либо обоих регистров cs и eip:

  •  если операнд в команде jmp — метка в текущем сегменте команд (a8, 16, 32), то ассемблер формирует машинную команду, операнд которой является значением со знаком, являющимся смещением перехода относительно следующей за jmp команды. При этом виде перехода изменяется только регистр eip/ip;
  •  если операнд в команде jmp — символический идентификатор ячейки памяти (m16, 32, 48), то ассемблер предполагает, что в ней находится адрес, по которому необходимо передать управление. Этот адрес может быть трех видов:
    •  значением абсолютного смещения метки перехода относительно начала сегмента кода. Размер этого смещения может быть 16 или 32 бит в зависимости от режима адресации;
    •  дальним указателем на метку перехода в реальном и защищенном режимах, содержащим два компонента адреса — сегментный и смещение. Размеры этих компонентов также зависят от установленного режима адресации (use16 или use32). Если текущим режимом является use16, то адрес сегмента и смещение занимают по 16 бит, причем смещение располагается в младшем слове двойного слова, отводимого под этот полный адрес метки перехода. Если текущим режимом является use32, то адрес сегмента и смещение занимают, соответственно, 16 и 32 бит, — в младшем двойном слове находится смещение, в старшем — адрес сегмента;
    •  адресом в одном из 16 или 32-разрядных регистров — этот адрес представляет собой абсолютное смещение метки, на которую необходимо передать управление, относительно начала сегмента команд.

Для понимания различий механизмов перехода в реальном и защищенном режимах нужно помнить следующее. В реальном режиме микропроцессор просто изменяет cs и eip/ip в соответствии с содержимым указателя в памяти. В защищенном режиме микропроцессор предварительно анализирует байт прав доступа AR в дескрипторе, номер которого определяется по содержимому сегментной части указателя. В зависимости от состояния байта AR микропроцессор выполняет либо переход, либо переключение задач.

Состояние флагов после выполнения команды (за исключением случая переключения задач): 

выполнение команды не влияет на флаги

Применение: 

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

См. также: урок 10, команды call, jcc

LAHF

(Load AH register from register Flags)

Загрузка регистра AH флагами из регистра eFlags/Flags

 

Схема команды: 

lahf 

Назначение: извлечение содержимого младшего байта регистра eflags/flags, в котором содержатся пять флагов: cf, pf, af, zf и sf.

Синтаксис 

Алгоритм работы: 

команда загружает регистр ah содержимым младшего байта регистра eflags/flags. Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Из-за того, что регистр флагов непосредственно недоступен, команду lahf можно применять для анализа и последующего изменения командой sahf состояния некоторых флагов регистра eflags/flags.

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

        lahf

        and     ah,11111110b

        sahf

        

См. также: команду sahf

LDS/LES/LFS/LGS/LSS

(Load pointer into ds/es/fs/gs/ss segment register)

Загрузка сегментного регистра ds/es/fs/gs/ss указателем из памяти

 

Схема команды: 

lds приемник,источник 

les приемник,источник 

lfs приемник,источник 

lgs приемник,источник 

lss приемник,источник 

Назначение: получение полного указателя в виде сегментной составляющей и смещения.

Синтаксис 

Алгоритм работы: 

Алгоритм работы команды зависит от действующего режима адресации (use16 или use32):

  •  если use16, то загрузить первые два байта из ячейки памяти источник в 16-разрядный регистр, указанный операндом приемник. Следующие два байта в области источник должны содержать сегментную составляющую некоторого адреса; они загружаются в регистр ds/es/fs/gs/ss;
  •  если use32, то загрузить первые четыре байта из ячейки памяти источник в 32-разрядный регистр, указанный операндом приемник. Следующие два байта в области источник должны содержать сегментную составляющую, или селектор, некоторого адреса; они загружаются в регистр ds/es/fs/gs/ss.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Таким образом, с помощью данных команд в паре регистров ds/es/fs/gs/ss и приемник оказывается полный адрес некоторой ячейки памяти. Это обстоятельство можно использовать, к примеру, при работе с цепочечными командами, где существуют жесткие соглашения на размещение адресов обрабатываемых строк. Помните, что любая загрузка сегментного регистра приводит к обновлению соответствующего теневого регистра (см. урок 16). Смотрите также описание команды cmps с примером использования.

См. также: уроки 5, 7, 11, команды lea и операторы ассемблера seg и offset

LEA

(Load Effective Address)

Загрузка эффективного адреса

 

Схема команды: 

lea приемник,источник 

Назначение: получение эффективного адреса (смещения) источника.

Синтаксис 

Алгоритм работы: 

алгоритм работы команды зависит от действующего режима адресации (use16 или use32):

  •  если use16, то в регистр приемник загружается 16-битное значение смещения операнда источник;
  •  если use32, то в регистр приемник загружается 32-битное значение смещения операнда источник.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Данная команда является альтернативой оператору ассемблера offset. В отличие от offset команда lea допускает индексацию операнда, что позволяет более гибко организовать адресацию операндов.

;загрузить в регистр bx адрес пятого

элемента массива mas

.data

mas     db      10 dup (0)

.code

...

        mov     di,4

        lea     bx,mas[di]

;или

        lea     bx,mas[4]

;или

        lea     bx,mas+4

        

См. также: уроки 5, 7, 11 и команды lea, lds, les, lss, lgs, lfs, операторы ассемблера seg и offset

LEAVE

(LEAVE from procedure)

Выход из процедуры

 

Схема команды: 

leave 

Назначение: удаление из стека области локальных (динамических) переменных, выделенной командой enter.

Синтаксис 

Алгоритм работы: 

команда выполняет обратные команде enter действия:

  •  содержимое ebp/bp копируется в esp/sp, тем самым восстанавливается значение esp/sp, которое было до вызова данной процедуры. С другой стороны, восстановление старого значения esp/sp означает освобождение пространства в стеке, отведенного для завершающейся процедуры (локальные переменные процедуры уничтожаются);
  •  из стека восстанавливается содержимое ebp/bp, которое было до входа в процедуру. После этого действия значение esp/sp также становится таким, каким оно было до входа в процедуру.

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

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команда leave не имеет операндов и выполняет обратные команде enter действия. Эта команда должна находиться непосредственно перед командой ret, которая в зависимости от соглашений конкретного языка по вызову процедур удаляет или не удаляет аргументы из стека (см. урок 14).

.286

proc1   proc

        enter   16,0

...

        leave

        ret

proc1   endp

        

См. также: урок 14 и команды enter, ret/retf

LGDT

(Load Global Descriptor Table)

Загрузка регистра глобальной дескрипторной таблицы

 

Схема команды: 

lgdt источник 

Назначение: загрузка регистра gdtr значениями базового адреса и размера глобальной дескрипторной таблицы GDT.

Синтаксис 

Алгоритм работы: 

команда выполняет загрузку 16 бит размера и 32 бит значения базового адреса начала таблицы GDT в памяти в системный регистр gdtr. Эта загрузка производится в соответствии с форматом этого регистра (см. урок 16). Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команду lgdt применяют при подготовке к переходу в защищенный режим для загрузки системного регистра gdtr. В качестве операнда в команде указывается адрес области в формате 16+32. Младшее слово области — размер GDT, двойное слово по старшему адресу — значение базового адреса начала этой таблицы. Данные два компонента должны быть сформированы в памяти заранее.

.286

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

point   STRUC

lim     dw      0

adr     dd      0

 ENDS

.data

point_gdt       point   

.code

...

;загружаем gdtr

        xor     eax,eax

        mov     ax,gdt_seg

        shl     eax,4

        mov     point_gdt.adr,eax

        lgdt    point_gdt

...

        

См. также: уроки 16, 17 и команду sgdt

LIDT

(Load Interrupt Descriptor Table)

Загрузка регистра глобальной дескрипторной таблицы

 

Схема команды: 

lidt источник 

Назначение: загрузка регистра idtr значениями базового адреса и размера глобальной дескрипторной таблицы IDT.

Синтаксис 

Алгоритм работы: 

Команда lidt аналогична lgdt, но для дескрипторной таблицы прерываний IDT (см. урок 17).

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команду lidt применяют при подготовке к переходу в защищенный режим для загрузки системного регистра idtr. В качестве операнда в команде указывается адрес области в формате 16+32. Младшее слово области — размер IDT, двойное слово по старшему адресу — значение базового адреса начала этой таблицы. Два данных компонента должны быть сформированы в памяти заранее.

.386

;структура для описания псевдодескрипторов gdtr и idtr

point   STRUC

lim     dw      0

adr     dd      0

 ENDS

.data

point_idt       point   

.code

...

;загружаем idtr

        xor     eax,eax

        mov     ax,IDT_SEG

        shl     eax,4

        mov     point_idt.adr,eax

        lidt    point_idt

...

        

См. также: урок 17 и команду sidt

LODS/LODSB/LODSW/LODSD

(LOad String Byte/Word/Double word operands)

Загрузка строки байтов/слов/двойных слов

 

Схема команды: 

lods источник 

lodsb 

lodsw 

lodsd

Назначение: загрузка элемента из последовательности (цепочки) в регистр-аккумулятор al/ax/eax.

Синтаксис 

Алгоритм работы: 

  •  загрузить элемент из ячейки памяти, адресуемой парой ds:esi/si, в регистр al/ax/eax. Размер элемента определяется неявно (для команды lods) или явно в соответствии с применяемой командой (для команд lodsb, lodsw, lodsd);
  •  изменить значение регистра si на величину, равную длине элемента цепочки. Знак этой величины зависит от состояния флага df:
    •  df=0 — значение положительное, то есть просмотр от начала цепочки к ее концу;
    •  df=1 — значение отрицательное, то есть просмотр от конца цепочки к ее началу.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команды извлекают элемент из ячейки памяти в один из регистров. Перед командой lods можно указать префикс повторения rep, но в этом нет особого смысла, так как обычно эту команду используют в некотором цикле для просмотра некоторой цепочки с элементами фиксированного размера.

str     db      ...

...

        cld

        lea     si,str

        lodsb   ;загрузить первый байт из str в al

        

См. также: урок 11 и команды ins/insb/insw/insd, cmps/cmpsb/cmpsw/cmpsd, movs/movsb/movsw/movsd, outs, scas/scasb/scasw/scasd, stos/stosb/stosw/stosd, rep/repe/repz/repne/repnz

LOOP

(LOOP control by register cx)

Управление циклом по cx

 

Схема команды: 

loop метка 

Назначение: организация цикла со счетчиком в регистре cx.

Синтаксис 

Алгоритм работы: 

  •  выполнить декремент содержимого регистра ecx/cx;
  •  анализ регистра ecx/cx:
    •  если ecx/cx=0, передать управление следующей за loop команде;
    •  если ecx/cx=1, передать управление команде, метка которой указана в качестве операнда loop.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команду loop применяют для организации цикла со счетчиком. Количество повторений цикла задается значением в регистре ecx/cx перед входом в последовательность команд, составляющих тело цикла. Помните о двух важных моментах:

  •  для предотвращения выполнения цикла при нулевом ecx/cx используйте команду jecxz/jcxz. Если этого не сделать, то при изначально нулевом ecx/cx цикл повторится 4 294 967 295/65 536 раз;
  •  смещение метки, являющейся операндом loop, не должно выходить из диапазона -128...+127 байт. Это смещение, как и в командах условного перехода, является относительным от значения счетчика адреса следующей за loop команды.

 

        mov     cx,10

...

        jcxz    m1

cycl:

;тело цикла

        loop    cycl

m1:

        

См. также: урок 10 и команды jecxz/jcxz, loope/loopz, loopne/loopnz

LOOPE/LOOPZ

LOOPNE/LOOPNZ

(LOOP control by register cx not equal 0 and ZF=1)

(LOOP control by register cx not equal 0 and ZF=0)

Управление циклом по cx c учетом значения флага ZF

 

Схема команды: 

loope/loopz метка 

loopne/loopnz метка 

Назначение: организация цикла со счетчиком в регистре cx с учетом флага zf.

Синтаксис 

Алгоритм работы: 

  •  выполнить декремент содержимого регистра ecx/cx;
  •  проанализировать регистр ecx/cx:
    •  если ecx/cx=0, передать управление следующей за loopxx команде;
    •  если ecx/cx=1, передать управление команде, метка которой указана в качестве операнда loopxx;
  •  анализ флага zf:
    •  если zf=0, для команд loope/loopz это означает выход из цикла, для команд loopne/loopnz — переход к началу цикла;
    •  если zf=1, для команд loope/loopz это означает переход к началу цикла, для команд loopne/loopnz — выход из цикла.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команды loopxx удобно использовать вместе с командами, которыe в результате своей работы меняют значение флага zf. Типичный пример — команда сравнения cmp.

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

str     db      'Найти первый пробел'

str_size=$-str

...

        cld

        mov     cx,str_size

        lea     si,str

cycl:

        lodsb

        cmp     al,' '

        loopne  cycl

        jcxz    m1      ;переход, если пробелов нет

        dec     si      ;в si — адрес пробела в строке str

...

m1

        

См. также: уроки 8, 10, 11 и команду loop

MOV

(MOVe operand)

Пересылка операнда

 

Схема команды: 

mov приемник,источник 

Назначение: пересылка данных между регистрами или регистрами и памятью.

Синтаксис 

Алгоритм работы: 

копирование второго операнда в первый операнд.

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

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

  •  направление пересылки в команде mov всегда справа налево, то есть из второго операнда в первый;
  •  значение второго операнда не изменяется;
  •  оба операнда не могут быть из памяти (при необходимости можно использовать цепочечную команду movs);
  •  лишь один из операндов может быть сегментным регистром;
  •  желательно использовать в качестве одного из операндов регистр al/ax/eax, так как в этом случае TASM генерирует более быструю форму команды mov.

 

        mov     al,5

        mov     bl,al

        mov     bx,ds

        

См. также: урок 10 и команды movs, lods/lodsb/lodsw/lodsd, stos/stosb,

stosw/stosd

MOV

(MOVe operand to/from system registers)

Пересылка операнда в системные регистры (или из них)

 

Схема команды: 

mov приемник,источник 

Назначение: пересылка данных между регистрами или регистрами и памятью.

Синтаксис 

Алгоритм работы: 

копирование второго операнда в первый.

Состояние флагов после выполнения команды: 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

r

r

r

r

r

Применение: 

Команда mov применяется для обмена данными между системными регистрами. Это одна из немногих возможностей доступа к содержимому этих регистров. Данную команду можно использовать только на нулевом уровне привилегий либо в реальном режиме работы микропроцессора.

.286

;переключение микропроцессора в защищенный

режим36:

        mov     eax,cr0

        bts     eax,0

        mov     cr0,eax

        

См. также: уроки 16, 17 и команды mov, bts

MOVS/MOVSB/MOVSW/MOVSD

(MOVe String Byte/Word/Double word)

Пересылка строк байтов/слов/двойных слов

 

Схема команды: 

movs приемник,источник 

movsb 

movsw 

movsd

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

Синтаксис 

Алгоритм работы: 

  •  выполнить копирование байта, слова или двойного слова из операнда источника в операнд приемник, при этом адреса элементов предварительно должны быть загружены:
    •  адрес источника — в пару регистров ds:esi/si (ds по умолчанию, допускается замена сегмента);
    •  адрес приемника — в пару регистров es:edi/di (замена сегмента не допускается);
  •  в зависимости от состояния флага df изменить значение регистров esi/si и edi/di:
    •  если df=0, то увеличить содержимое этих регистров на длину структурного элемента последовательности;
    •  если df=1, то уменьшить содержимое этих регистров на длину структурного элемента последовательности;
  •  если есть префикс повторения, то выполнить определяемые им действия (см. команду rep).

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команды пересылают элемент из одной ячейки памяти в другую. Размеры пересылаемых элементов зависят от применяемой команды. Команда movs может работать с элементами размером в байт, слово, двойное слово. В качестве операндов в команде указываются идентификаторы последовательностей этих элементов в памяти. Реально эти идентификаторы используются лишь для получения типов элементов последовательностей, а их адреса должны быть предварительно загружены в указанные выше пары регистров. Транслятор, обработав команду movs и выяснив тип операндов, генерирует одну из машинных команд movsb, movsw или movsd. Машинного аналога для команды movs нет. Для адресации операнда приемник обязательно должен использоваться регистр es.

Для того чтобы эти команды можно было использовать для пересылки последовательности элементов, имеющих размерность байт, слово, двойное слово, необходимо использовать префикс rep. Префикс rep заставляет циклически выполняться команды пересылки до тех пор, пока содержимое регистра ecx/cx не станет равным нулю.

str1    db      'str1 копируется в str2'

len_str1=$-str1

a_str1  dd      str1

str2    db      len_str1 dup (' ')

a_str2  dd      str2

...

        mov     cx,len_str1

        lds     si,str1

        les     di,str2

        cld

rep     movsb

        

См. также: урок 11 и команды cmps/cmpsb/cmpsw/cmpsd, ins/insb/insw/insd, lods/lodsb/lodsw/lodsd, outs, scas/scasb/scasw/scasd, stos/stosb/stosw/stosd, rep/repe/repz/repne/repnz

MOVSX

(MOVe and Sign eXtension)

Пересылка со знаковым расширением

 

Схема команды: 

movsx приемник,источник 

Назначение: преобразование элементов со знаком меньшей размерности в эквивалентные им элементы со знаком большей размерности.

Синтаксис 

Алгоритм работы: 

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

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

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

        mov     al,0ffh

        movsx   bx,al   ;bx=0ffffh

        

См. также: урок 8 и команды mov, movs, movzx, cbw, cwd, cdq 

MOVZX

(MOVe and Zero eXtension)

Пересылка с нулевым расширением

 

Схема команды: 

movzx приемник,источник 

Назначение: преобразование элементов без знака меньшей размерности в эквивалентные им элементы без знака большей размерности.

Синтаксис 

Алгоритм работы: 

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

Состояние флагов после выполнения команды: 

выполнение команды не влияет на флаги

Применение: 

Команду movzx обычно используют для получения эквивалентного, но большего по размеру операнда без учета знака. Она может быть использована для согласования операндов различной размерности. Но не следует думать, что все эти разнотипные пересылки делает одна машинная команда. На самом деле существует несколько машинных команд, каждая из которых работает со своими размерами операндов. Генерацию же нужной команды обеспечивает транслятор на основе анализа исходного текста программы.

.data

sl      db      ?

.code

...

        mov     al,0ffh

        movzx   bx,al   ;bx=00ffh

...

;или из памяти:

        movzx   eax,byte ptr sl

        

См. также: урок 8 и команды mov, movs/movsb/

movsw/movsd, movsx, cbw, cwd, cdq

MUL

(MULtiply)

Умножение целочисленное без учета знака

 

Схема команды: 

mul множитель_1 

Назначение: операция умножения двух целых чисел без учета знака.

Синтаксис 

Алгоритм работы: 

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

  •  если операнд, указанный в команде — байт, то второй сомножитель должен располагаться в al;
  •  если операнд, указанный в команде — слово, то второй сомножитель должен располагаться в ax;
  •  если операнд, указанный в команде — двойное слово, то второй сомножитель должен располагаться в eax.

Результат умножения помещается также в фиксированное место, определяемое размером сомножителей:

  •  при умножении байтов результат помещается в ax;
  •  при умножении слов результат помещается в пару dx:ax;
  •  при умножении двойных слов результат помещается в пару edx:eax.

Состояние флагов после выполнения команды (если старшая половина результата нулевая): 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

0

?

?

?

0

Состояние флагов после выполнения команды (если старшая половина результата ненулевая): 

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF

1

?

?

?

1

Применение: 

Команда mul выполняет целочисленное умножение операндов без учета их знаковых разрядов. Для этой операции необходимо наличие двух операндов-сомножителей, размещение одного из которых фиксировано, а другого задается операндом в команде. Контролировать размер результата удобно используя флаги cf и of.

mn_1    db      15

mn_2    db      25

...

        mov     al,mn_1

        mul     mn_2

        

См. также: урок 8 и команду imul 

NEG

(NEGate operand)

Изменить знак операнда

 

Схема команды: 

neg источник 

Назначение: изменение знака (получение двоичного дополнения) источника.

Синтаксис 

Алгоритм работы: 

  •  выполнить вычитание (0 – источник) и поместить результат на место источника;
  •  если источник=0, то его значение не меняется.

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

11

07

06

04

02

00

OF

SF

ZF

AF

PF

CF