Справочник по языку Ассемблера IBM PC

         

PUSH


(PUSH operand onto stack)

Размещение операнда в стеке

 

Схема команды:  push источник 

Назначение: размещение содержимого операнда источник в стеке.


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

уменьшить значение указателя стека esp/sp на 4/2 (в зависимости от значения атрибута размера адреса — use16 или use32);

записать источник в вершину стека (адресуемую парой ss:esp/sp).

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

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

Применение:


Команда push используется совместно с командой pop для записи значений в стек и извлечения их из стека. Размер записываемых значений — слово или двойное слово. Также в стек можно записывать непосредственные значения. Заметьте, что в отличие от команды pop в стек можно включать значение сегментного регистра cs. Другой интересный момент связан с регистром sp. Команда push esp/sp записывает в стек значение esp/sp по состоянию до выдачи этой команды. В микропроцессоре i8086 по этой команде записывалось скорректированное значение sp. При записи в стек 8-битных значений для них все равно выделяется слово или двойное слово (в зависимости от use16 или use32).



my_proc proc    near         push    ax         push    bx ;тело процедуры, в которой изменяется содержимое ;регистров ax и bx ...         pop     bx         pop     ax         ret         endp         

См. также: уроки 7, 10, 14, 15, 16, 17 и команды , , , , , , , ,



PUSHA


(PUSH All general registers onto stack)

Размещение всех регистров общего назначения в стеке

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

Назначение: размещение в стеке регистров общего назначения в следующей последовательности: ax, cx, dx, bx, sp, bp, si, di.


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

уменьшить значение указателя стека esp/sp на 32/16 (в зависимости от значения атрибута размера адреса — use16 или use32);

включить в стек последовательно значения регистров общего назначения ax, cx, dx, bx, sp, bp, si, di.

Содержимое di при этом будет на вершине стека. В стек помещается содержимое sp по состоянию до выполнения команды.
Состояние флагов после выполнения команды:

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

Применение:


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

my_proc proc    near         pusha ;тело процедуры, в которой изменяется ;содержимое регистров общего назначения ...         popa         ret         endp         

См. также: уроки 7, 10, 14, 15, 16, 17 и команды , , , , , , , ,



PUSHAD


(PUSH All general Double word registers onto stack)

Размещение всех регистров общего назначения в стеке

 

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

Назначение: размещение в стеке регистров общего назначения в следующей последовательности: eax, ecx, edx, ebx, esp, ebp, esi, edi.


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

уменьшить значение указателя стека esp на 32;

включить в стек последовательно значения регистров общего назначения eax, ecx, edx, ebx, esp, ebp, esi, edi. Содержимое edi при этом будет на вершине стека. Содержимое esp включается по состоянию на момент, предшествовавший выполнению данной команды.

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

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

Применение:


Команда pushad используется совместно с командой popad для сохранения и восстановления всех регистров общего назначения. Эти команды используются аналогично командам popa и pusha.

.386 my_proc proc    near         pushad ;тело процедуры, в которой изменяется ;содержимое регистров общего назначения ...         popad         ret         endp         

См. также: уроки 7, 10, 14, 15, 16, 17 и команды , , , , , , , ,



PUSHF


(PUSH Flags register onto stack)

Размещение регистра флагов в стеке

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

Назначение: размещение в вершине стека (ss:sp) содержимого регистра флагов flags.


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

уменьшить значение указателя стека sp на 2;

поместить в вершину стека содержимое регистра flags.

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

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

Применение:


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

;извлечь значение регистра flags и изменить ;значение флага cf на обратное         pushf         pop     ax         xor     ax,01h         push    ax         popf         

См. также: уроки 7, 10, 14, 15, 16, 17 и команды , , , , , , , ,



PUSHFD


(PUSH eFlags Double word register onto stack)

Размещение расширенного регистра флагов в стеке

 

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

Назначение: размещение в стеке содержимого регистра флагов eflags.


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

уменьшить значение указателя стека esp на 4;

записать в вершину стека двойное слово, представляющее собой содержимое регистра eflags.

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

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

Применение:


Команды pushfd и popfd используются аналогично командам pushf и popf. Команда pushfd применяется для получения содержимого регистра флагов. Как известно, прямой доступ к регистру флагов невозможен, поэтому данная команда является одной из немногих команд, позволяющих получить доступ к регистру флагов как к содержимому обычного регистра. Обратное действие, то есть восстановление — возможно измененного слова — в регистр флагов, осуществляется командой popfd. Эта команда может использоваться в программах обработки прерываний или в других случаях, когда необходимо сохранить локальный контекст процесса вычисления.

.386 ;извлечь значение регистра eflags и изменить ;значение флага cf на обратное         pushfd         pop     eax         xor     eax,01h         push    eax         popfd         

См. также: уроки 7, 10, 14, 15, 16, 17 и команды , , , , , , , ,



Работа с записями


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

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

width имя_элемента_записи ;значением оператора будет размер элемента в битах.

width имя_экземпляра_записи или width имя_типа_записи ;значением оператора будет размер всей записи в битах.

mov al,width i2 ... mov ax,width iotest

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

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

Выделение элемента записи:


Поместить запись во временную память — регистр (8, 16 или 32-битный в зависимости от размера записи). Получить битовую маску, соответствующую элементу записи, с помощью оператора mask. Локализовать биты в регистре с помощью маски и команды . Сдвинуть биты элемента к младшим разрядам регистра командой . Число разрядов для сдвига получить с использованием имени элемента записи.

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

Работа с элементом записи:


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

Помещение измененного элемента на его место в запись:


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

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

В качестве примера рассмотрим листинг 8, который обнуляет поле i2 в записи iotest.
Листинг 8. Работа с полем записи ;prg_12_7.asm masm model small stack 256 iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00 .data flag iotest <>

.code main: mov ax,@data mov ds,ax mov al,mask i2 shr al,i2 ;биты i2 в начале ax and al,0fch ;обнулили i2 ;помещаем i2 на место shl al,i2 mov bl,[flag] xor bl,mask i2 ;сбросили i2 or bl,al ;наложили exit: mov ax,4c00h ;стандартный выход int 21h end main ;конец программы

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




RCL


(Rotate operand through Carry flag Left)

Циклический сдвиг операнда влево через флаг переноса

Схема команды:  rcl операнд,количество_сдвигов 

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


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

сдвиг всех битов операнда влево на один разряд, при этом старший бит операнда становится значением флага переноса cf;

одновременно старое значение флага переноса cf вдвигается в операнд справа и становится значением младшего бита операнда;

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

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

11 00
OF CF
?r

Здесь обозначение ?r означает то, что анализ состояния флага имеет смысл при определенном сочетании операндов. В случае команды rcl флаг of представляет интерес, если сдвиг осуществляется на один разряд (см. ниже описание применения команды rcl).
Применение:


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

of=1, если текущее значение флага cf и выдвигаемого бита операнда слева различны;

of=0, если текущее значение флага cf и выдвигаемого бита операнда слева совпадают.

 

;сдвиг операнда, занимающего два двойных слова ;на четыре разряда влево ch_l    dd      ...     ;младшая часть 64-битного операнда ch-2    dd      ...     ;старшая часть 64-битного операнда ...         mov     cx,4    ;счетчик сдвигов в cx         mov     eax,ch_l         mov     edx,ch_h m1:     clc             ;очистка флага cf         rcl     eax,1   ;старший бит eax в cf         rcl     edx,1   ;cf в младший бит edx, старший бит edx в cf         loop    m1         

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



RCR


(Rotate operand through Carry flag Right)

Циклический сдвиг операнда вправо через флаг переноса

 

Схема команды:  rcr операнд,количество_сдвигов 

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


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

сдвиг всех битов операнда вправо на один разряд; при этом младший бит операнда становится значением флага переноса cf;

одновременно старое значение флага переноса — в операнд слева и становится значением старшего бита операнда;

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

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

11 00
OF CF
?r

Здесь обозначение ?r означает то, что анализ состояния флага имеет смысл при определенном сочетании операндов. В случае команды rcr флаг of представляет интерес, если сдвиг осуществляется на один разряд (см. ниже описание применения команды rcr).
Применение:


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

of=1, если текущие (то есть до операции сдвига) значения флага cf и старшего, левого бита операнда различны;

of=0, если текущие (то есть до операции сдвига) значения флага cf и старшего, левого бита операнда слева совпадают.


 
;подсчет числа единичных битов в операнде operand dw      ... ...         mov     cx,16   ;размер операнда         xor     al,al   ;счетчик единичных битов cycl:   rcr     operand,1         jc      $+4     ;переход, если очередной выдвинутый бит равен 1         jmp     $+4     ;переход, если очередной выдвинутый бит равен 0         inc     al      ;увеличить счетчик единичных битов         loop    cycl         

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


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


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

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

eax/ax/ah/al (Accumulator register) — аккумулятор.
Применяется для хранения промежуточных данных. В некоторых командах использование этого регистра обязательно; ebx/bx/bh/bl (Base register) — базовый регистр.
Применяется для хранения базового адреса некоторого объекта в памяти; ecx/cx/ch/cl (Count register) — регистр-счетчик.
Применяется в командах, производящих некоторые повторяющиеся действия. Его использование зачастую неявно и скрыто в алгоритме работы соответствующей команды.
К примеру, команда организации цикла кроме передачи управления команде, находящейся по некоторому адресу, анализирует и уменьшает на единицу значение регистра 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) — регистр указателя базы кадра стека.
Предназначен для организации произвольного доступа к данным внутри стека.

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



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


Это очень интересная группа регистров, предназначенных для аппаратной отладки. Средства аппаратной отладки впервые появились в микропроцессоре 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 соответственно).
Первый бит из каждой пары — это так называемое локальное разрешение; его установка говорит о том, что точка прерывания действует если она находится в пределах адресного пространства текущей задачи.
Второй бит в каждой паре определяет глобальное разрешение, которое говорит о том, что данная контрольная точка действует в пределах адресных пространств всех задач, находящихся в системе; тип доступа, по которому инициируется прерывание: только при выборке команды, при записи или при записи/чтении данных. Биты, определяющие подобную природу возникновения прерывания, локализуются в старшей части данного регистра.

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



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


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

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

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

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

регистра таблицы глобальных дескрипторов 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 — сохранять текущее состояние задачи в момент переключения на другую задачу.



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


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

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

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

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

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

8 флагов состояния. Эти флаги могут изменяться после выполнения машинных команд.
Флаги состояния регистра eflags

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

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

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


Таблица 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 Используется в защищенном режиме работы микропроцессора для фиксации того факта, что одна задача вложена в другую



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


В группу регистров управления входят 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 Кбайт.



REP/REPE/REPZ/REPNE/REPNZ


(REPeat string operation)

Повторить цепочечную операцию

Схема команды:  rep 
repe 
repz 
repne 
repnz

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


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


Алгоритм работы зависит от конкретного префикса. Префиксы rep, repe и repz на самом деле имеют одинаковый код операции, их действия зависят от той цепочечной команды, которую они предваряют:

rep используется перед следующими цепочечными командами и их краткими эквивалентами: movs, stos, ins, outs. Действия rep:

анализ содержимого cx:

если cx<>0, то выполнить цепочечную команду, следующую за данным префиксом и перейти к шагу 2;

если cx=0, то передать управление команде, следующей за данной цепочечной командой (выйти из цикла по rep);

уменьшить значение cx=cx–1 и вернуться к шагу 1;

repe и repz используются перед следующими цепочечными командами и их краткими эквивалентами: cmps, scas. Действия repe и repz:

анализ содержимого cx и флага zf:

если cx<>0 или zf<>0, то выполнить цепочечную команду, следующую за данным префиксом, и перейти к шагу 2;

если cx=0 или zf=0, то передать управление команде, следующей за данной цепочечной командой (выйти из цикла по rep);

уменьшить значение cx=cx-1 и вернуться к шагу 1;

repne и repnz также имеют один код операции и имеют смысл при использовании перед следующими цепочечными командами и их краткими эквивалентами: cmps, scas. Действия repne и repnz:

анализ содержимого cx и флага zf:

если cx<>0 или zf=0, то выполнить цепочечную команду, следующую за данным префиксом и перейти к шагу 2;

если cx=0 или zf<>0, то передать управление команде, следующей за данной цепочечной командой (выйти из цикла по rep);

уменьшить значение cx=cx–1 и вернуться к шагу 1.

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

06
ZF
r

Применение:


Команды rep, repe, repz, repne и repnz в силу специфики своей работы называются префиксами. Они имеют смысл только при использовании цепочечных операций, заставляя их циклически выполняться и тем самым без организации внешнего цикла обрабатывать последовательности элементов фиксированной длины. Большинство применяемых префиксов являются условными, то есть они прекращают работу цепочечной команды при выполнении определенных условий.
См. также: урок 11 и команды , , , ,



RET/RETF


(RETurn/RETurn Far from procedure)

Возврат ближний (дальний) из процедуры

 

Схема команды:  ret 
ret число

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


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


Работа команды зависит от типа процедуры:

для процедур ближнего типа — восстановить из стека содержимое eip/ip;

для процедур дальнего типа — последовательно восстановить из стека содержимое eip/ip и сегментного регистра cs.

если команда ret имеет операнд, то увеличить содержимое esp/sp на величину операнда число; при этом учитывается атрибут режима адресации — use16 или use32:

если use16, то sp=(sp+число), то есть указатель стека сдвигается на число байт, равное значению число;

если use32, то sp=(sp+2*число), то есть указатель стека сдвигается на число слов, равное значению число.

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

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

Применение:


Команду ret необходимо применять для возврата управления вызывающей программе из процедуры, управление которой было передано по команде call. На самом деле микропроцессор имеет три варианта команды возврата ret - это ret, ее синоним retn, а также команда retf. Они отличаются типами процедур, в которых используются. Команды ret и retn служат для возврата из процедур ближнего типа. Команда retf — команда возврата для процедур дальнего типа. Какая конкретно команда будет использоваться, определяется компилятором; программисту лучше использовать команду ret и доверить транслятору самому сгенерировать ее ближний или дальний вариант. Количество команд ret в процедуре должно соответствовать количеству точек выхода из нее.
Некоторые языки высокого уровня, к примеру Pascal, требуют, чтобы вызываемая процедура очищала стек от переданных ей параметров. Для этого команда ret содержит необязательный параметр число, который, в зависимости от установленного атрибута размера адреса, означает количество байт или слов, удаляемых из стека по окончании работы процедуры.

my_proc proc ...         ret     6         endp         

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



ROL


(Rotate operand Left)

Циклический сдвиг операнда влево

Схема команды:  rol операнд,количество_сдвигов 

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


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

сдвиг всех битов операнда влево на один разряд, при этом старший бит операнда вдвигается в операнд справа и становится значением младшего бита операнда;

одновременно выдвигаемый бит становится значением флага переноса cf;

указанные выше два действия повторяются количество раз, равное значению второго операнда.

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

11 00
OF CF
?r

Применение:


Команда rol используется для циклического сдвига разрядов операнда влево. Отличие этого сдвига от rcl в том, что очередной сдвигаемый бит одновременно вдвигается в операнд справа и становится значением флага cf. Так же, как и для других сдвигов, значение второго операнда (счетчика сдвига) ограничено диапазоном 0...31. Это объясняется тем, что микропроцессор использует только пять младших разрядов операнда количество_разрядов. Аналогично другим командам сдвига сохраняется эффект, связанный с поведением флага of, значение которого имеет смысл только в операциях сдвига на один разряд:

если of=1, то текущее значение флага cf и выдвигаемого слева бита операнда различны;

если of=0, то текущее значение флага cf и выдвигаемого слева бита операнда совпадают.

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

;поменять местами половинки регистра eax:         mov     ax,0ffff0000h         mov     cl,16         rol     eax,cl  ;eax=0000ffffh         

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



ROR


Циклический сдвиг операнда вправо

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

 

Схема команды:  ror операнд,количество_сдвигов 

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


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

сдвиг всех битов операнда вправо на один разряд, при этом младший бит операнда вдвигается в операнд слева и становится значением старшего бита операнда;

одновременно этот младший бит операнда становится значением флага переноса cf;

старое значение флага переноса cf вдвигается в операнд слева и становится значением старшего бита операнда;

указанные выше два действия повторяются количество раз, равное значению второго операнда.

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

11 00
OF CF
?r

Применение:


Команда ror используется для циклического сдвига разрядов операнда вправо. Отличие этого сдвига от rcr в том, что очередной сдвигаемый бит одновременно вдвигается в операнд слева и становится значением флага cf. Так же, как и для других сдвигов, значение второго операнда (счетчика сдвига) ограничено диапазоном 0...31. Это объясняется тем, что микропроцессор использует только пять младших разрядов операнда количество_разрядов. Аналогично другим командам сдвига сохраняется эффект, связанный с поведением флага of, значение которого имеет смысл только в операциях сдвига на один разряд:

если of=1, то текущее значение флага cf и вдвигаемого слева бита операнда различны;

если of=0, то текущее значение флага cf и вдвигаемого слева бита операнда совпадают;

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

;поместить четыре младших бита ax на место старших битов:         ror     ax,4         

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



SAHF


(Store AH register into register Flags)

Загрузка регистра флагов eFlags/Flags из регистра AH

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

Назначение: запись содержимого регистра ah в младший байт регистра eflags/flags, в котором содержатся пять флагов cf, pf, af, zf и sf.


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


Команда загружает младший байт регистра eflags/flags содержимым регистра ah. В битах 7, 6, 4, 2 и 0 регистра ah должны, соответственно, содержаться новые значения флагов sf, zf, af, pf и cf.
Состояние флагов после выполнения команды:

07 06 04 02 00
SF ZF AF PF CF
r r r r

Применение:


Эта команда используется совместно с командой lahf. Из-за того, что регистр флагов непосредственно недоступен, сочетание этих команд можно применять для анализа — и, возможно, изменения — состояния некоторых флагов в регистре eflags/flags. Содержимое старшей части регистра флагов не изменяется.

;сбросить в ноль флаг cf         lahf         and     ah,11111110b         sahf         

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



SAL


(Shift Arithmetic operand Left)

Сдвиг арифметический операнда влево

 

Схема команды:  sal операнд,количество_сдвигов 

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


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

сдвиг всех битов операнда влево на один разряд, при этом выдвигаемый слева бит становится значением флага переноса cf;

одновременно справа в операнд вдвигается нулевой бит;

указанные выше два действия повторяются количество раз, равное значению второго операнда.

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

11 00
OF CF
?r

Применение:


Команда sal используется для сдвига разрядов операнда влево. Так же, как и для других сдвигов, значение второго операнда (счетчика сдвига) ограничено диапазоном 0...31. Это объясняется тем, что микропроцессор использует только пять младших разрядов количество_разрядов. Аналогично другим командам сдвига сохраняется эффект, связанный с поведением флага of, значение которого имеет смысл только в операциях сдвига на один разряд:

если of=1, то текущее значение флага cf и выдвигаемого слева бита операнда различны;

если of=0, то текущее значение флага cf и выдвигаемого слева бита операнда совпадают.

Этот эффект, как вы помните, обусловлен тем, что флаг cf устанавливается в единицу всякий раз при изменении знакового разряда операнда.
Команду sal удобно использовать для умножения целочисленных операндов без знака на степени 2. Кстати сказать, это самый быстрый способ такого умножения; умножить содержимое ax на 16 (2 в степени 4):

        mov     ax,17         sal     ax,4         

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



SAR


(Shift Arithmetic operand Right)

Сдвиг арифметический операнда вправо

Схема команды:  sar операнд,количество_сдвигов 

Назначение: арифметический сдвиг операнда вправо.


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

сдвиг всех битов операнда вправо на один разряд, при этом выдвигаемый справа бит становится значением флага переноса cf;

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

указанные выше два действия повторяются количество раз, равное значению второго операнда.

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

11 00
OF CF
?r

Применение:


Команда sar используется для арифметического сдвига разрядов операнда вправо. Так же, как и для других сдвигов, значение второго операнда (счетчика сдвига) ограничено диапазоном 0...31. Это объясняется тем, что микропроцессор использует только пять младших разрядов операнда количество_разрядов. В отличие от других команд сдвига флаг of всегда сбрасывается в ноль в операциях сдвига на один разряд.
Команду sar можно использовать для деления целочисленных операндов со знаком на степени 2.

        mov     ax,88 ;(ax) разделить на 2 во второй степени, то есть на 4         sar     ax,2         

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



SBB


(SuBtract with Borrow)

Вычитание с заемом

 

Схема команды:  sbb операнд_1,операнд_2 

Назначение: целочисленное вычитание с учетом результата предыдущего вычитания командами sbb и sub (по состоянию флага переноса cf).


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

выполнить сложение операнд_2=операнд_2+(cf);

выполнить вычитание операнд_1=операнд_1-операнд_2;

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

11 07 06 04 02 00
OF SF ZF AF PF CF
r r r r r

Применение:


Команда sbb используется для выполнения вычитания старших частей значений многобайтных операндов с учетом возможного предыдущего заема при вычитании младших частей значений этих операндов.

;выполнить вычитание 64-битных значений: vich_1-vich_2 vich_1  dd      2 dup (0) vich_2  dd      2 dup (0) rez     dd      2 dup (0) ... ;ввести значения в поля vich_1 и vich_2: ;младший байт по младшему адресу ...         mov     eax,vich_1         sub     eax,vich_2      ;вычесть младшие половинки чисел         mov     rez,eax ;младшая часть результата         mov     eax,vich_1+4         sbb     eax,vich_2+4    ;вычесть старшие половинки чисел         mov     rez+4,eax       ;старшая часть результата         

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



SCAS/SCASB/SCASW/SCASD


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

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

Схема команды:  scas приемник 
scasb 
scasw 
scasd

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


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

выполнить вычитание (элемент цепочки-(eax/ax/al)). Элемент цепочки локализуется парой es:edi/di. Замена сегмента es не допускается;

по результату вычитания установить флаги;

изменить значение регистра edi/di на величину, равную длине элемента цепочки. Знак этой величины зависит от состояния флага df:

df=0 — величина положительная, то есть просмотр от начала цепочки к ее концу;

df=1 — величина отрицательная, то есть просмотр от конца цепочки к ее началу.

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

11 07 06 04 02 00
OF SF ZF AF PF CF
r r r r r

Применение:


Команды сканирования сравнивают значение в регистре eax/ax/al с ячейкой памяти, локализуемой парой регистров es:edi/di. Размер сравниваемого элемента зависит от применяемой команды. Команда scas может работать с элементами размером в байт, слово или двойное слово. В качестве операнда в команде указывается идентификатор последовательности элементов в памяти. Реально этот идентификатор используется лишь для получения типа элементов последовательности, а ее адрес должен быть предварительно загружен в указанную выше пару регистров. Транслятор, обработав команду scas и выяснив тип операндов, генерирует одну из машинных команд:: scasb, scasw или scasd. Машинного аналога для команды scas нет. Для адресации операнда источник обязательно должен использоваться регистр es.
Для того чтобы эту команду можно было использовать для поиска значения в последовательности элементов, имеющих размерность байт, слово или двойное слово, необходимо использовать один из префиксов repe или repne. Эти префиксы не только заставляют циклически выполняться команду поиска, пока ecx/cx<>0, но и отслеживают состояние флага zf (см. команды rep/repe/repne).

;сосчитать число пробелов в строке str .data str     db      '...' len_str=$-str .code         mov     ax,@data         mov     ds,ax         mov     es,ax         lea     di,str         mov     cx,len_str      ;длину строки — в cx         mov     al,' '         mov     bx,0    ;счетчик для подсчета пробелов в строке         cld cycl: repe    scasb         jcxz    exit    ;переход на exit, если цепочка просмотрена полностью         inc     bx         jmp     cycl exit:   ...         

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



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


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

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

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

в команде.
Адреса дополнительных сегментов данных должны содержаться в регистрах es, gs, fs (extension data segment registers).



SETcc


(byte SET on condition)

Установка байта по условию

 

Схема команды:  setcc операнд 

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


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


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

Команды установки байтов

Команда Проверяемые флаги Логическое условие
SETA/SETNBE CF = 0 и ZF = 0 (выше)/(не ниже или равно)
SETAE/SETNB CF = 0 (выше или равно)/(не ниже)
SETB/SETNAE  CF = 1 (ниже)/(не выше или равно)
SETBE/SETNA CF = 1 или ZF = 1 (ниже или равно)/(не выше)
SETC CF = 1 перенос
SETE/SETZ ZF = 1 ноль
SETG/SETNLE ZF = 0 или SF = OF (больше)/(не меньше или равно)
SETGE/SETNL SF = OF (больше или равно)/(не меньше)
SETL/SETNGE SF <> OF если SF <> OF
SETLE/SETNG ZF=1 или SF <> OF (меньше или равно)/(не больше)
SETNC CF = 0 нет переноса
SETNE/SETNZ ZF = 0 не равно нулю
SETNO OF=0 нет переполнения
SETNP/SETPO PF = 0 (неравенство)/(нет контроля четности)
SETNS SF = 0 нет знака, число положительное
SETO OF = 1 переполнение
SETP/SETPE PF = 1 контроль четности/равенство
SETS SF = 1 если знак минус, число отрицательное

Если проверяемое условие (или содержимое соответствующих флагов на момент выдачи команды setcc) истинно, то установить значение операнда в 01h, если условие ложно — то в 00h.
Состояние флагов после выполнения команды:

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

Применение:


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

;подсчитать число единичных битов в регистре ax         mov     cx,16 m1:     rol     ax,1         setc    bl         add     bh,bl         clc         loop    m1         

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



SGDT


(Store Global Descriptor Table)

Сохранение регистра глобальной дескрипторной таблицы

Схема команды:  sgdt источник 

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


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


Команда выполняет чтение содержимого системного регистра gdtr в область памяти размером 48 бит. Структурно эти 48 бит представляют 16 бит размера и 32 бита значения базового адреса начала таблицы GDT в памяти.
Состояние флагов после выполнения команды:

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

Применение:


Команду sgdt применяют при работе системных программ с уровнем привилегий 0, в частности, при написании различных драйверов.

.286 ;структура для описания псевдодескриптора gdtr point   STRUC lim     dw      0 adr     dd      0         ENDS .data point_gdt       point   

.code ... ;читаем содержимое gdtr         sgdt    point_gdt ...         

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



SHL


(SHift logical Left)

Сдвиг логический операнда влево

Схема команды:  shl операнд,количество_сдвигов 

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


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

сдвиг всех битов операнда влево на один разряд, при этом выдвигаемый слева бит становится значением флага переноса cf;

одновременно слева в операнд вдвигается нулевой бит;

указанные выше два действия повторяются количество раз, равное значению второго операнда.

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

11 00
OF CF
?r

Применение:


Команда shl используется для сдвига разрядов операнда влево. Ее машинный код идентичен коду sal, поэтому вся информация, приведенная для sal, относится и к команде shl. Команда shl используется для сдвига разрядов операнда влево. Так же, как и для других сдвигов, значение второго операнда (счетчикк сдвига) ограничено диапазоном 0...31. Это объясняется тем, что микропроцессор использует только пять младших разрядов операнда количество_разрядов. Аналогично другим командам сдвига сохраняется эффект, связанный с поведением флага of, значение которого имеет смысл только в операциях сдвига на один разряд:

если of=1, то текущее значение флага cf и выдвигаемого слева бита операнда различны;

если of=0, то текущее значение флага cf и выдвигаемого слева бита операнда совпадают.

Этот эффект, как вы помните, обусловлен тем, что флаг of устанавливается в единицу всякий раз при изменении знакового разряда операнда.
Команду shl удобно использовать для умножения целочисленных операндов без знака на степени 2. Кстати сказать, это самый быстрый способ умножения;  умножить содержимое ax на 16 (2 в степени 4).

        mov     ax,17         shl     ax,4         

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



SHLD


(SHift Left Double word)

Сдвиг двойного слова влево

 

Схема команды:  shld приемник,источник,количество_сдвигов 

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


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

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

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

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

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

11 07 06 04 02 00
OF SF ZF AF PF CF
? r r ? r

Применение:


Команда shld используется для манипуляции битовыми строками длиной до 64 бит. Эту команду удобно использовать для быстрой вставки (или извлечения) битной строки в большую битную строку; при этом, что очень важно, не разрушается контекст (битное окружение) этих подстрок.

.386 ;извлечь старшую половину eax в bx без разрушения eax         mov     cl,16         shld    ebx,eax,cl         push    bx         shl     ebx,cl         shld    eax,ebx,cl      ;восстановим eax pop bx         

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



SHR


Сдвиг логический операнда вправо

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

Схема команды:  shr операнд,кол-во_сдвигов 

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


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

сдвиг всех битов операнда вправо на один разряд; при этом выдвигаемый справа бит становится значением флага переноса cf;

одновременно слева в операнд вдвигается нулевой бит;

указанные выше два действия повторяются количество раз, равное значению второго операнда.

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

11 07 06 04 02 00
OF SF ZF AF PF CF
?r r r ? r

Применение:


Команда shr используется для логического сдвига разрядов операнда вправо. Так же, как и для других сдвигов, значение второго операнда (счетчика сдвига) ограничено диапазоном 0...31. Это объясняется тем, что микропроцессор использует только пять младших разрядов операнда количество_разрядов. В отличие от других команд сдвига, флаг of всегда сбрасывается в ноль в операциях сдвига на один разряд.
Команду shr можно использовать для деления целочисленных операндов без знака на степени 2.

        mov     cl,4         shr     eax,cl  ;(eax) разделить на 2 в степени 4         

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



SHRD


(SHift Right Double word)

Сдвиг двойного слова вправо

 

Схема команды:  shrd приемник,источник,количество_сдвигов 

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


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

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

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

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

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

11 07 06 04 02 00
OF SF ZF AF PF CF
? r r ? r

Применение:


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

.386 ;разделить операнд размером 64 бит на степень 2 op_l    dd      ...     ;младшая часть операнда op_h    dd      ...     ;старшая часть операнда ...         mov     eax,op_h         shrd    op_l,eax,4      ;разделить операнд на 4 ;так как старшая часть операнда реально еще не сдвинулась, ;то нужно привести ее в соответствие с результатом         shr     op_h,4         

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



SIDT


(Store Interrupt Descriptor Table)

Сохранение регистра глобальной дескрипторной таблицы прерываний

 

Схема команды:  sidt источник 

Назначение: извлечение содержимого системного регистра idtr, содержащего значения базового адреса и размера дескрипторной таблицы прерываний IDT.


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


команда sidt выполняет чтение содержимого системного регистра idtr в область памяти размером 48 бит. Структурно эти 48 бит представляют 16 бит размера и 32 бита значения базового адреса начала таблицы IDT в памяти.
Состояние флагов после выполнения команды:

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

Применение:


Команду sidt применяют при работе системных программ с уровнем привилегий 0, в частности, при написании различных драйверов. В качестве операнда в команде указывается адрес области в формате 16+32. Младшее слово области — размер IDT, двойное слово по старшему адресу — значение базового адреса начала этой таблицы.

.286 ;структура для описания псевдодескрипторов gdtr и idtr point   STRUC lim     dw      0 adr     dd      0         ENDS .data point_idt       point   

.code ... ;читаем содержимое idtr         sidt    point_idt ...         

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



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


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

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

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

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

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

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

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

код операции (КОП) и директива — это мнемонические обозначения соответствующей машинной команды, макрокоманды или директивы транслятора;

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

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


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


Допустимыми символами при написании текста программ являются:
все латинские буквы: A—Z, a—z. При этом заглавные и строчные буквы считаются эквивалентными;
цифры от 0 до 9;
знаки ?, @, $, _, &;
разделители , . [ ] ( ) < > { } + / * % ! ' " ? \ = # ^.
Предложения ассемблера формируются из лексем, представляющих собой синтаксически неразделимые последовательности допустимых символов языка, имеющие смысл для транслятора.
Лексемами являются:
идентификаторы — последовательности допустимых символов, использующиеся для обозначения таких объектов программы, как коды операций, имена переменных и названия меток. Правило записи идентификаторов заключается в следующем: идентификатор может состоять из одного или нескольких символов. В качестве символов можно использовать буквы латинского алфавита, цифры и некоторые специальные знаки — _, ?, $, @. Идентификатор не может начинаться символом цифры. Длина идентификатора может быть до 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 ;команда с регистровыми операндами

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

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

Выражения представляют собой комбинации операндов и операторов, рассматриваемые как единое целое.
Результатом вычисления выражения может быть адрес некоторой ячейки памяти или некоторое константное (абсолютное) значение.
Возможные типы операндов мы уже рассмотрели. Перечислим теперь возможные типы операторов ассемблера и синтаксические правила формирования выражений ассемблера.
В приведены поддерживаемые языком ассемблера операторы и перечислены их приоритеты. Дадим краткую характеристику операторов:
Арифметические операторы. К ним относятся:
унарные “+” и “–”;
бинарные “+” и “–”;
умножения “*”;
целочисленного деления “/”;
получения остатка от деления “mod”.
Эти операторы расположены на уровнях приоритета 6, 7, 8 в . Например,

     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 и ). Логическое значение “истина” соответствует цифровой единице, а “ложь” — нулю. Например,

      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

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


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

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

четыре ; четыре ; восемь .



Сообщения о фатальных ошибках


 
 

Bad switch

Неверный параметр-переключатель командной строки.

Can't find @file __

Не найден файл подсказок __.

Сan't locate file __

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

Error writing to listing file

Ошибка при записи в файл листинга. Возможно, просто исчерпано место на диске.

Error writing to object file

Ошибка при записи в объектный файл. Возможно, просто исчерпано место на диске.

File not found

Не найден файл. В командной строке указано имя несуществующего исходного файла.

File was changed or deleted while assembly in progress

Файл был изменен или уничтожен в процессе ассемблирования.

Insufficient memory to process command line

Не хватает памяти для обработки командной строки.

Internal error

Внутренняя ошибка.

Invalid command line

Недопустимая командная строка

Invalid number after _

Недопустимый номер после _.

Out of hash space

Не хватает памяти под хеш-таблицы. Для каждого имени идентификатора в программе транслятор формирует один элемент таблицы идентификаторов. Эта таблица рассчитана на 16 384 имени идентификаторов. При необходимости это число можно увеличить, используя параметр командной строки /kh.

Out of memory

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

Out of string space

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

Too many errors found

Обнаружено слишком много ошибок. Трансляция прекращена, так как в исходном файле содержится слишком много ошибок.

Unexpected end of file (no END directive)

Неожиданный конец файла (отсутствует директива END).



Сообщения об ошибках


32-bit segment not allowed without .386

32-битовые флаги без директивы .386 не допускаются.

Argument needs type override

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

Argument to operation or instruction has illegal size

Операнд операции или команды имеет недопустимый размер.

Arithmetic overflow

Арифметическое переполнение. Потеря значащих цифр при вычислении значения выражения.

ASSUME must be segment register

В директиве ASSUME должен быть указан сегментный регистр.

Bad keyword in SEGMENT statement

Неверное ключевое слово в операторе SEGMENT. Один из параметров директивы SEGMENT: тип выравнивания, тип объединения или тип сегмента, — имеет недопустимое значение.

Can't add relative quantities

Нельзя складывать относительные адреса.

Can't address with currently ASSUMEd segment registers

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

Can't convert to pointer

Невозможно преобразование в указатель.

Can't emulate 8087 instruction

Невозможна эмуляция команд сопроцессора 8087.

Can't make variable public

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

Can't override ES segment

Нельзя переопределить сегмент ES. Это сообщение характерно для операций типа цепочечных. В некоторых из них нельзя переопределять местоположение сегментной части адреса операнда.

Can't subtract dissimilar relative quantities


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

Can't use macro name in expression

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

Can't use this outside macro

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

Code or data emission to undeclared segment

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

Constant assumed to mean Immediate const

Константа интерпретируется как непосредственная.

Constant too large

Слишком большая константа. Константа превышает допустимую для данного режима величину. Например, числа, большие 0ffffh, можно использовать, если только директивой .386/.386P или .486/.486Р разрешены команды процессора i386 или i486.1

CS not correctly assumed

Некорректное значение в регистре cs.

CS override in protected mode

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

CS unreachable from current segment

CS недостижим из текущего сегмента. При определении метки кода с помощью двоеточия (:) или с помощью директив LABEL или PROC сегментный регистр не указывает на текущий кодовый сегмент или группу, содержащую текущий кодовый сегмент.

Declaration needs name

В директиве объявления не указано имя.

Directive ignored in Turbo Pascal model

В режиме TPASCAL директива игнорируется.

Directive not allowed inside structure definition

Недопустимая директива внутри определения структуры.

Duplicate dummy arguments:

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

Expecting METHOD keyword

Требуется ключевое слово METHOD.

Expecting offset quantity

Требуется указать величину смещения.

Expecting offset or pointer quantity



Требуется указать смещение или указатель.

Expecting pointer type

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

Expecting record field name

Требуется имя поля записи. Инструкция SETFIELD или GETFIELD использована без последующего имени поля.

Expecting register ID

Требуется идентификатор регистра

Expecting scalar type

Операнд должен быть константой.

Expecting segment or group quantity

Должно быть указано имя сегмента или группы.

Extra characters on line

Лишние символы в строке.

Forward reference needs override

Ошибка при использовании умолчания для ссылки вперед.

Global type doesn't match symbol type

Тип, указанный в директиве GLOBAL, не совпадает с действительным типом имени идентификатора.

ID not member of structure

Идентификатор не является полем структуры.

Illegal forward reference

Недопустимая ссылка вперед.

Illegal immediate

Недопустим непосредственный операнд.

Illegal indexing mode

Недопустимый режим индексации.

Illegal instruction

Недопустимая команда.

Illegal instruction for currently selected processor(s)

Недопустимая команда для выбранного в настоящий момент процессора.

Illegal local argument

Недопустимый локальный параметр.

Illegal local symbol prefix

Недопустимый префикс для локальных имен идентификаторов.

Illegal mаcro argument

Недопустимый параметр макрокоманды.

Illegal memory reference

Недопустима ссылка на память.

Illegal number

Недопустимое число.

Illegal origin address

Недопустимый начальный адрес.

Illegal override in structure

Недопустимое переопределение в структуре.

Illegal override register

Недопустимое переопределение регистра.

Illegal radix

Недопустимое основание системы счисления. В директиве .RADIX в качестве основания системы счисления указано недопустимое число. Основанием системы счисления могут быть только числа: 2, 8, 10 и 16. Эти числа интерпретируются как десятичные независимо от текущей системы счисления.

Illegal register for instruction



Недопустимый регистр в инструкции. В качестве операнда инструкций SETFIELD и GETFIELD использован недопустимый регистр.

Illegal register multiplier

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

Illegal segment address

Недопустимый сегментный адрес.

Illegal use of constant

Недопустимо использование константы.

Illegal use of register

Недопустимо использование регистра.

Illegal use of segment register

Недопустимо использование сегментного регистра.

Illegal USES register

В директиве USES указан недопустимый регистр.

Illegal version ID

Недопустимый идентификатор версии.

Illegal warning ID

Недопустимый идентификатор предупреждающего сообщения.

Instruction can be compacted with override

Возможно сокращение длины команды, если явно указать тип имени. Из-за наличия ссылки вперед на имя идентификатора объектный код содержит дополнительные команды NOP. Этим самым транслятор резервирует место для размещения адреса идентификатора. При необходимости код можно сократить, убрав ссылку вперед, либо явно указать тип символического имени.

Invalid model type

Недопустимая модель памяти.

Invalid operand(s) to instruction

Недопустимый операнд(ы) для данной команды.

Labels can't start with numeric characters

Метки не могут начинаться с цифровых символов.

Line too long - truncated

Строка слишком длинная, и поэтому производится усечение.

Location counter overflow

Переполнение счетчика адреса.

Method call requires object name

В вызове метода необходимо имя объекта.

Missing argument list

Отсутствует список аргументов.

Missing argument or <

Отсутствует аргумент либо не указана угловая скобка <.

Missing argument size variable

Отсутствует переменная для размера блока параметров.

Missing COMM ID

Отсутствует идентификатор в директиве COMM.

Missing dummy argument

Отсутствует формальный параметр.

Missing end quote

Отсутствует закрывающая кавычка.

Missing macro ID

Отсутствует идентификатор макрокоманды.

Missing module name

Отсутствует имя модуля.

Missing or illegal type specifier



Отсутствует или неверно указан спецификатор типа.

Missing table member ID

Пропущен идентификатор элемента таблицы.

Missing term in list

Отсутствует член в списке параметров.

Missing text macro

Отсутствует текстовая макрокоманда.

Model must be specified first

Сначала должна быть указана модель памяти.

Module is pass-dependant — compatibility pass was done

Модуль зависит от прохода. Выполнен проход, обеспечивающий совместимость с MASM.

Name must come first

Имя должно быть указано первым.

Near jump or call to different CS

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

Need address or register

Требуется указать адрес или регистр.

Need colon

Требуется двоеточие.

Need expression

Требуется указать выражение.

Need file name after INCLUDE

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

Need left parenthesis

Отсутствует левая круглая скобка.

Need method name

Требуется имя метода.

Need pointer expression

Требуется выражение-указатель.

Need quoted string

Требуется указать строку в кавычках.

Need register in expression

В выражении требуется указать имя регистра.

Need right angle bracket

Отсутствует правая угловая скобка.

Need right curly bracket

Требуется правая фигурная скобка.

Need right parenthesis

Отсутствует правая круглая скобка.

Need right square bracket

Отсутствует правая квадратная скобка.

Need stack argument

Не указан стековый параметр в команде арифметики с плавающей запятой.

Need structure member name

Не указано имя поля структуры.

Not expecting group or segment quantity

Использование имени группы или сегмента недопустимо.

One non-null field allowed per union expansion

При расширении объединения допускается указывать только одно поле непустым.

Only one startup sequence allowed

Допускается только одна директива генерации кода инициализации.

Open conditional

Открытый условный блок. После завершающей программу директивы END обнаружен незакрытый условно ассемблируемый блок, открытый одной из директив IFxxx.



Open procedure

Открытая процедура. После завершающей программу директивы END обнаружен незакрытый директивой ENDР блок описания процедуры, открытый где-то в программе директивой PROC.

Open segment

Открытый сегмент. После завершающей программу директивы END обнаружен незакрытый директивой ENDS сегмент, открытый где-то в программе директивой SEGMENT.

Open structure definition

Не указан конец определения структуры (директива ENDS).

Operand types do not match

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

Operation illegal for static table member

Для статического элемента таблицы операция не допускается.

Pass-dependant construction encountered

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

Pointer expression needs brackets

Адресное выражение должно быть заключено в квадратные скобки.

Positive count expecting

Счетчик должен быть положительным.

Record field too large

Слишком длинное поле в записи.

Record member not found

Не найден статический элемент записи.

Recursive definition not allowed for EQU

Рекурсивное определение недопустимо в директиве EQU.

Register must be AL or AX

Допустимо указание только регистра al или ax.

Register must be DX

Допустимо указание только регистра dx.

Relative jump out of range by __ bytes

Адрес назначения условного перехода превышает допустимый предел на __ байт.

Relative quantity illegal

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

Reserved word used as symbol

Зарезервированное слово используется в качестве имени идентификатора.

Rotate count must be constant or CL

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

Rotate count out of range

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

Segment alignment not strict enough



Выравнивание сегмента недостаточно точное.

Segment attributes illegally redefined

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

Segment name is superfluous

Имя сегмента игнорируется.

String too long

Слишком длинная строка. Длина указанной в кавычках строки превышает 255 символов.

Symbol already defined: __

Имя идентификатора уже определено.

Symbol already different kind

Имя идентификатора уже объявлено с другим типом.

Symbol has no width or mask

Имя идентификатора не может быть использовано в операциях WIDTH и MASK.

Symbol is not a segment or already part of a group

Имя идентификатора не является именем сегмента либо уже определено в группе.

Text macro expansion exceeds maximum line length

Расширение текстовой макрокоманды превышает максимально допустимую длину.

Too few operands to instruction

В команде не хватает операндов.

Too many errors or warnings

Слишком много ошибок или предупреждений. Число сообщений об ошибках превысило максимально возможное число — 100.

Too many initial values

Слишком много начальных значений.

Too many register multipliers in expression

В выражении содержится слишком много множителей для регистров.

Too many registers in expression

В выражении указано слишком много регистров.

Too many USES registers

Слишком много регистров в директиве USES.

Trailling null value assumed

Предполагается конечное пустое значение.

Undefined symbol

Идентификатор не определен.

Unexpected end of file (no END directive)

Неожиданный конец файла (нет директивы END).

Unknown character

Неизвестный символ.

Unmatched ENDP:_

Непарная директива ENDP:_.

Unmatched ENDS:_

Непарная директива ENDS:_.

User-generated error

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

USES has no effect without language

USES игнорируется без спецификации языка.

Value out of range

Значение константы превышает допустимое значение.


STC


(Set Carry Flag)

Установка флага переноса

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

Назначение: установка флага переноса cf в 1.


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


установить флаг cf в единицу.

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

00
CF
1

Применение:


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

...         stc             ;cf=1 ...         

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



STD


(SeT Direction Flag)

Установка флага направления

 

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

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


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


установить флаг df в единицу.

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

10
DF
1

Применение:


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

...         std             ;df=1 ;смотрите материал урока 11         

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



STI


(SeT Interrupt flag)

Установка флага прерывания

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

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


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


установить флаг if в единицу.

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

09
IF
1

Применение:


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

...         sti             ;if=1         

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



STOS/STOSB/STOSW/STOSD


(Store String Byte/Word/Double word operands)

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

 

Схема команды:  stos приемник 
stosb 
stosw 
stosd

Назначение: сохранение элемента из регистра-аккумулятора al/ax/eax в последовательности (цепочке).


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

записать элемент из регистра al/ax/eax в ячейку памяти, адресуемую парой es:di/edi. Размер элемента определяется неявно (для команды stos) или конкретной применяемой командой (для команд stosb, stosw, stosd);

изменить значение регистра di на величину, равную длине элемента цепочки. Знак этого изменения зависит от состояния флага df:

df=0 — увеличить, что означает просмотр от начала цепочки к ее концу;

df=1 — уменьшить, что означает просмотр от конца цепочки к ее началу.

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

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

Применение:


Команды сохраняют элемент из регистров al/ax/eax в ячейку памяти. Перед командой stos можно указать префикс повторения rep, в этом случае появляется возможность работы с блоками памяти, заполняя их значениями в соответствии с содержимым регистра ecx/cx.

;заполнить некоторую область памяти пробелами str     db      'Какая-то строка' len_str=$-str ...         mov     ax,@data         mov     ds,ax         mov     es,ax         cld         mov     al,' '         lea     di,str         mov     cx,len_str rep     stosb           ;заполняем пробелами строку str         

 

;пример совместной работы stosb и lodsb: ;копировать одну строку в другую до первого пробела str1    db      'Какая-то строка' len_str1=$-str str2    db      len_str1 dup (' ') ...         mov     ax,@data         mov     ds,ax         mov     es,ax         cld         mov     cx,len_str1         lea     si,str1         lea     di,str2 m1:     lodsb         cmp     al,' '         jc      exit    ;выход, если пробел         stosb         loop    m1 exit:         

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



Структура программы на ассемблере


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

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

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


В процессе трансляции инструкции ассемблера преобразуются в соответствующие команды системы команд микропроцессора;

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

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

строки комментариев, содержащие любые символы, в том числе и буквы русского алфавита. Комментарии игнорируются транслятором.



Структуры


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

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

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

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

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

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



SUB


(SUBtract)

Вычитание

Схема команды:  sub операнд_1,операнд_2 

Назначение: целочисленное вычитание.


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

выполнить вычитание операнд_1=операнд_2-операнд_1;

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

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

11 07 06 04 02 00
OF SF ZF AF PF CF
r r r r r

Применение:


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

;выполнить вычитание 64-битных значений: vich_1-vich_2 vich_1  dd      2 dup (0) vich_2  dd      2 dup (0) rez     dd      2 dup (0) ... ;ввести значения в поля vich_1 и vich_2: ;младший байт по младшему адресу ...         mov     eax,vich_1         sub     eax,vich_2      ;вычесть младшие половинки чисел         mov     rez,eax ;младшая часть результата         mov     eax,vich_1+4         sbb     eax,vich_2+4    ;вычесть старшие половинки чисел         mov     rez+4,eax       ;старшая часть результата         

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



TEST


(TEST operand)

Логическое И

 

Схема команды:  test приемник,источник 

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


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

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

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

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

11 07 06 02 00
OF SF ZF PF CF
0 r r 0

Применение:


Команда test используется для логического умножения двух операндов. Результат операции, в отличие от команды and, никуда не записывается, устанавливаются только флаги. Эту команду удобно использовать для получения информации о состоянии заданных битов операнда приемник. Для анализа результата используется флаг zf, который равен 1, если результат логического умножения равен нулю.

        test    al,01h         jnz     m1      ;переход, если нулевой бит al равен 1         

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



Типовые операции с массивами


Для демонстрации основных приемов работы с массивами лучше всего подходят программы поиска или сортировки.

Рассмотрим одну такую программу, выполняющую сортировку массива по возрастанию (листинг 6).

Листинг 6. Сортировка массива ;prg_12_5.asm MASM MODEL small STACK 256 .data mes1 db 0ah,0dh,'Исходный массив — $',0ah,0dh ;некоторые сообщения mes2 db 0ah,0dh,'Отсортированный массив — $',0ah,0dh n equ 9 ;количество элементов в массиве, считая с 0 mas dw 2,7,4,0,1,9,3,6,5,8 ;исходный массив tmp dw 0 ;переменные для работы с массивом i dw 0 j dw 0 .code main: mov ax,@data mov ds,ax xor ax,ax ;вывод на экран исходного массива mov ah,09h lea dx,mes1 int 21h ;вывод сообщения mes1 mov cx,10 mov si,0 show_primary: ;вывод значения элементов ;исходного массива на экран mov dx,mas[si] add dl,30h mov ah,02h int 21h add si,2 loop show_primary

;строки 40-85 программы эквивалентны следующему коду на языке С: ;for (i=0;i ; for (j=9;j>i;j--) ; if (mas[i]>mas[j]) ; {tmp=mas[i]; ; mas[i]=mas[j]; ; mas[j]=tmp;} mov i,0 ;инициализация i ;внутренний цикл по j internal: mov j,9 ;инициализация j jmp cycl_j ;переход на тело цикла exchange: mov bx,i ;bx=i shl bx,1 mov ax,mas[bx] ;ax=mas[i] mov bx,j ;bx=j shl bx,1 cmp ax,mas[bx] ;mas[i] ? mas[j] — сравнение элементов jle lesser ;если mas[i] меньше, то обмен не нужен и ;переход на продвижение далее по массиву ;иначе tmp=mas[i], mas[i]=mas[j], mas[j]=tmp: ;tmp=mas[i] mov bx,i ;bx=i shl bx,1 ;умножаем на 2, так как элементы — слова mov tmp,ax ;tmp=mas[i]

;mas[i]=mas[j] mov bx,j ;bx=j shl bx,1 ;умножаем на 2, так как элементы — слова mov ax,mas[bx] ;ax=mas[j] mov bx,i ;bx=i shl bx,1 ;умножаем на 2, так как элементы — слова mov mas[bx],ax ;mas[i]=mas[j]

;mas[j]=tmp mov bx,j ;bx=j shl bx,1 ;умножаем на 2, так как элементы — слова mov ax,tmp ;ax=tmp mov mas[bx],ax ;mas[j]=tmp lesser: ;продвижение далее по массиву во внутреннем цикле dec j ;j-- ;тело цикла по j cycl_j: mov ax,j ;ax=j cmp ax,i ;сравнить j ? i jg exchange ;если j>i, то переход на обмен ;иначе на внешний цикл по i inc i ;i++ cmp i,n ;сравнить i ? n — прошли до конца массива jl internal ;если i

;вывод отсортированного массива mov ah,09h lea dx,mes2 int 21h prepare: mov cx,10 mov si,0 show: ;вывод значения элемента на экран mov dx,mas[si] add dl,30h mov ah,02h int 21h add si,2 loop show exit: mov ax,4c00h ;стандартный выход int 21h end main ;конец программы

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



Типы данных


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

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

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

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

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




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

байт — восемь последовательно расположенных битов, пронумерованных от 0 до 7, при этом бит 0 является самым младшим значащим битом; слово — последовательность из двух байт, имеющих последовательные адреса. Размер слова — 16 бит; биты в слове нумеруются от 0 до 15. Байт, содержащий нулевой бит, называется младшим байтом, а байт, содержащий 15-й бит - старшим байтом. Микропроцессоры Intel имеют важную особенность — младший байт всегда хранится по меньшему адресу. Адресом слова считается адрес его младшего байта. Адрес старшего байта может быть использован для доступа к старшей половине слова. двойное слово — последовательность из четырех байт (32 бита), расположенных по последовательным адресам. Нумерация этих бит производится от 0 до 31. Слово, содержащее нулевой бит, называется младшим словом, а слово, содержащее 31-й бит, - старшим словом. Младшее слово хранится по меньшему адресу. Адресом двойного слова считается адрес его младшего слова. Адрес старшего слова может быть использован для доступа к старшей половине двойного слова. учетверенное слово — последовательность из восьми байт (64 бита), расположенных по последовательным адресам. Нумерация бит производится от 0 до 63. Двойное слово, содержащее нулевой бит, называется младшим двойным словом, а двойное слово, содержащее 63-й бит, — старшим двойным словом. Младшее двойное слово хранится по меньшему адресу. Адресом учетверенного слова считается адрес его младшего двойного слова. Адрес старшего двойного слова может быть использован для доступа к старшей половине учетверенного слова.






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

Целый тип со знаком — двоичное значение со знаком, размером 8, 16 или 32 бита. Знак в этом двоичном числе содержится в 7, 15 или 31-м бите соответственно. Ноль в этих битах в операндах соответствует положительному числу, а единица — отрицательному. Отрицательные числа представляются в дополнительном коде. Числовые диапазоны для этого типа данных следующие:



8-разрядное целое — от –128 до +127; 16-разрядное целое — от –32 768 до +32 767; 32-разрядное целое — от –231 до +231–1.

Целый тип без знака — двоичное значение без знака, размером 8, 16 или 32 бита. Числовой диапазон для этого типа следующий:

байт — от 0 до 255; слово — от 0 до 65 535; двойное слово — от 0 до 232–1.

Указатель на память двух типов:



ближнего типа — 32-разрядный логический адрес, представляющий собой относительное смещение в байтах от начала сегмента. Эти указатели могут также использоваться в сплошной (плоской) модели памяти, где сегментные составляющие одинаковы; дальнего типа — 48-разрядный логический адрес, состоящий из двух частей: 16-разрядной сегментной части — селектора, и 32-разрядного смещения.

Цепочка — представляющая собой некоторый непрерывный набор байтов, слов или двойных слов максимальной длины до 4 Гбайт. Битовое поле представляет собой непрерывную последовательность бит, в которой каждый бит является независимым и может рассматриваться как отдельная переменная. Битовое поле может начинаться с любого бита любого байта и содержать до 32 бит. Неупакованный двоично-десятичный тип — байтовое представление десятичной цифры от 0 до 9. Неупакованные десятичные числа хранятся как байтовые значения без знака по одной цифре в каждом байте. Значение цифры определяется младшим полубайтом. Упакованный двоично-десятичный тип представляет собой упакованное представление двух десятичных цифр от 0 до 9 в одном байте. Каждая цифра хранится в своем полубайте. Цифра в старшем полубайте (биты 4–7) является старшей.





Рис. 2. Основные логические типы данных микропроцессора

Отметим, что “Зн” на рис. 2 означает знаковый бит.

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

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

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

Директивы резервирования и инициализации данных простых типов имеют формат, показанный на рис. 3.






На рис. 3 использованы следующие обозначения:

? показывает, что содержимое поля не определено, то есть при задании директивы с таким значением выражения содержимое выделенного участка физической памяти изменяться не будет. Фактически, создается неинициализированная переменная; значение инициализации — значение элемента данных, которое будет занесено в память после загрузки программы. Фактически, создается инициализированная переменная, в качестве которой могут выступать константы, строки символов, константные и адресные выражения в зависимости от типа данных. Подробная информация приведена в приложении 1; выражение — итеративная конструкция с синтаксисом, описанным на рис. 5.17. Эта конструкция позволяет повторить последовательное занесение в физическую память выражения в скобках n раз. имя — некоторое символическое имя метки или ячейки памяти в сегменте данных, используемое в программе.

На рис. 3 представлены следующие поддерживаемые TASM директивы резервирования и инициализации данных:

db — резервирование памяти для данных размером 1 байт.
Директивой db можно задавать следующие значения:

выражение или константу, принимающую значение из диапазона:

для чисел со знаком –128...+127; для чисел без знака 0...255;

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

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

выражение или константу, принимающую значение из диапазона:

для чисел со знаком –32 768...32 767; для чисел без знака 0...65 535;

выражение, занимающее 16 или менее бит, в качестве которого может выступать смещение в 16-битовом сегменте или адрес сегмента; 1- или 2-байтовую строку, заключенная в кавычки. dd — резервирование памяти для данных размером 4 байта.
Директивой dd можно задавать следующие значения:



выражение или константу, принимающую значение из диапазона:

для i8086:

для чисел со знаком –32 768...+32 767; для чисел без знака 0...65 535;

для i386 и выше:

для чисел со знаком –2 147 483 648...+2 147 483 647; для чисел без знака 0...4 294 967 295;

Рис. 4. Окно дампа памяти для программы листинга 1

Обсудим рис. 4. На нем вы видите данные вашего сегмента в двух представлениях: шестнадцатеричном и символьном. Видно, что со смещением 0000 расположены символы, входящие в строку message. Она занимает 34 байта. После нее следует байт, имеющий в сегменте данных символическое имя perem_1, содержимое этого байта offh.
Теперь обратите внимание на то, как размещены в памяти байты, входящие в слово, обозначенное символическим именем perem_2. Сначала следует байт со значением 7fh, а затем со значением 3ah. Как видите, в памяти действительно сначала расположен младший байт значения, а затем старший.
Теперь посмотрите и самостоятельно проанализируйте размещение байтов для поля, обозначенного символическим именем perem_3.
Оставшуюся часть сегмента данных вы можете теперь проанализировать самостоятельно.
Остановимся лишь на двух специфических особенностях использования директив резервирования и инициализации памяти. Речь идет о случае использования в поле операндов директив dw и dd символического имени из поля имя этой или другой директивы резервирования и инициализации памяти. В нашем примере сегмента данных это директивы с именами adr и adr_full.
Когда транслятор встречает директивы описания памяти с подобными операндами, то он формирует в памяти значения адресов тех переменных, чьи имена были указаны в качестве операндов. В зависимости от директивы, применяемой для получения такого адреса, формируется либо полный адрес (директива dd) в виде двух байтов сегментного адреса и двух байтов смещения, либо только смещение (директива dw). Найдите в дампе на рис. 4 поля, соответствующие именам adr и adr_full, и проанализируйте их содержимое.

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

Сегмент (seg) — адрес начала сегмента, содержащего переменную; Смещение (offset) в байтах от начала сегмента с переменной; Тип (type) — определяет количество памяти, выделяемой переменной в соответствии с директивой объявления переменной.

Получить и использовать значение этих атрибутов в программе можно с помощью рассмотренных нами операторов ассемблера ,

и .



TASM поддерживает следующие сложные типы данных:

; ; ; .

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




Архитектура персонального компьютера


Архитектура ЭВМ


Набор регистров


Организация памяти


Типы данных


Формат команд


Обработка прерываний



Создание программы на ассемблере


Создание объектного модуля (трансляция программы)


Создание загрузочного модуля (компоновка программы)


Отладчик Turbo Debugger



Структура программы на ассемблере


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


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


Описание простых типов данных ассемблера



Макросредства языка ассемблера


Псевдооператоры equ и =


Макрокоманды


Макродирективы


Директивы условной компиляции


Константные выражения в условных директивах


Дополнительное управление трансляцией



Модульное программирование


Технологии программирования


Процедуры в языке ассемблера


Связь ассемблера с языками высокого уровня



Прерывания


Контроллер прерываний


Программирование контроллера прерываний i8259А


Реальный режим работы микропроцессора



Защищенный режим работы микропроцессора


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


Структуры данных защищенного режима


Пример программы защищенного режима



Обработка прерываний в защищенном режиме


Шлюз ловушки


Шлюз прерывания


Шлюз задачи


Программирование контроллера прерываний i8259A


Загрузка регистра IDTR



Условная генерация пользовательской ошибки


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

и и и и


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



Вложенность директив условной трансляции


Как мы неоднократно видели в приведенных выше примерах, TASM допускает вложенность условных директив компиляции. Более того, так как вложенность требуется довольно часто, TASM предоставляет набор дополнительных директив формата ELSEIFxxx, которые заменяют последовательность подряд идущих ELSE и IFxxx

в структуре:

IFxxx ; ELSE IFxxx ;... ENDIF ENDIF

Эту последовательность условных директив можно заменить эквивалентной последовательностью дополнительных директив:

IFxxx ;... ELSEIFxxx ;... ENDIF

Наличие xxx в ELSExxx говорит о том, что каждая из директив IF, IFB, IFIDN и т. д. имеет аналогичную директиву ELSEIF, ELSEIFB, ELSEIFIDN и т. д.
В конечном итоге это улучшает читаемость кода. В последнем примере фрагмента макроса, проверяющем, имя какого регистра было передано в макрос, наблюдается подобная ситуация. Последовательность ELSE и IFDIFI можно записать так, как в строке 4:

show macro rg ifdifi ,

goto M_al elseifdifi ,

goto M_ah else exitm endif :M_al ... :M_ah ... endm



XADD


(eXchange and ADD)

Обмен и сложение

Схема команды:  xadd приемник,источник 

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


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

копировать содержимое операнда приемник в операнд источник;

выполнить сложение (приемник+источник);

поместить сумму в операнд приемник.

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

11 07 06 04 02 00
OF SF ZF AF PF CF
r r r r r

Применение:


Команда xadd используется для выполнения операции обмена и сложения двух операндов.

        mov     al,08h         mov     bl,01h         xadd    al,bl   ;al=09h, bl=08h         

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



XCHG


(eXCHanGe)

Обмен

 

Схема команды:  xchg операнд_1,операнд_2 

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


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


обмен содержимого операнд_1 и операнд_2.

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

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

Применение:


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

;поменять порядок следования байт в слове ch1     label   byte         dw      0f85ch ...         mov     al,ch1         xchg    ch1+1,al         mov     ch1,al         

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



XLAT/XLATB


(transLATe Byte from table)

Преобразование байта

Схема команды:  xlat адрес_таблицы_байтов xlatb 

Назначение: подмена байта в регистре al байтом из последовательности (таблицы) байтов в памяти.


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

вычислить адрес, равный ds:bx+(al);

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

Несмотря на наличие операнда адрес_таблицы_байтов в команде xlat, адрес последовательности байтов, из которой будет осуществляться выборка байта для подмены в регистре al, должен быть предварительно загружен в пару ds:bx(ebx). Команда xlat допускает замену сегмента.

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

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

Применение:


Команду xlat можно использовать для выполнения перекодировок символов. Для формирования адреса таблицы в регистрах bx(ebx) можно использовать команду lea или оператор ассемблера offset в команде mov.

table   db      'abcdef' int     db      0       ;значение индекса ...         mov     al,3         lea     bx,table         xlat            ;(al)='c'         

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



XOR


Логическое исключающее ИЛИ

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

 

Схема команды:  xor приемник,источник 

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


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

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

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

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

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

11 07 06 04 02 00
OF SF ZF AF PF CF
0 r r ? 0

Применение:


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

;изменить значение бита 0 регистра al на обратное         xor     al,01h         

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



Записи


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

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

Запись — структурный тип данных, состоящий из фиксированного числа элементов длиной от одного до нескольких бит.
При описании записи для каждого элемента указывается его длина в битах и, что необязательно, некоторое значение.
Суммарный размер записи определяется суммой размеров ее полей и не может быть более 8, 16 или 32 бит.
Если суммарный размер записи меньше указанных значений, то все поля записи “прижимаются” к младшим разрядам.

Использование записей в программе, так же, как и структур, организуется в три этапа:

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

Компилятор TASM, кроме стандартных средств обработки записей, поддерживает также и некоторые

их обработки.



Записи: дополнительные возможности обработки


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

Для установки значения некоторого поля записи используется команда setfield с синтаксисом:

setfield имя_элемента_записи назначение,регистр_источник

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

getfield имя_элемента_записи регистр_назначение, источник

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

сдвиг содержимого регистр_источник влево на количество разрядов, соответствующее расположению элемента в записи; логическую операцию над операндами назначение и регистр_источник. Результат операции помещается в операнд назначение.

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

Действие команды getfield обратно setfield. В качестве операнда источник может быть указан либо регистр либо адрес памяти.
В регистр, указанный операндом регистр_назначение, помещается результат работы команды — значение элемента записи.
Интересная особенность связана с регистр_назначение. Команда getfield всегда использует 16-битный регистр, даже если вы укажете в этой команде имя 8-битного регистра.

В качестве примера применения команд setfield и getfield рассмотрим листинг 9.

Листинг 9. Работа с полями записи ;prg_12_8.asm masm model small stack 256 iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00 .data flag iotest <>

.code main: mov ax,@data mov ds,ax mov al,flag mov bl,3 setfield i5 al,bl xor bl,bl getfield i5 bl,al mov bl,1 setfield i4 al,bl setfield i5 al,bl exit: mov ax,4c00h ;стандартный выход int 21h end main ;конец программы

<

При установке значений полей не


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

В заключение хотелось бы привести еще один пример использования записей.
Это описание регистра eflags. Для удобства мы разбили это описание на три части:

eflags_1_7 — младший байт eflags/flags; eflags_8_15 — второй байт eflags/flags; eflags_h — старшая половина eflags.

eflags_l_7 record sf7:1=0,zf6:1=0,c5:1=0,af4:1=0,c3:1=0,pf2:1=0,c1:=1,cf0:1=0 eflags_l_15 record c15:1=0,nt14:1=0,iopl:2=0,of11:1=0,df10:1=0,if9:1=1,tf8:1=0 eflags_h record c:13=0,ac18:1=0,vm17:1=0,rf16:1=0

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