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

         

AAA


(Ascii Adjust after Addition)

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

 

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

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


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

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

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

увеличить значение al на 6;

очистить старший полубайт регистра al;

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

установить флаги: af = 1, cf = 1,

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

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



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

Применение:


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


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

        mov     ah,08h  ;ah=08h         mov     al,05h  ;al=05h         add     al,ah   ;al=al+ah=05h+08h=0dh — не BCD-число         xor     ah,ah   ;ah=0         aaa             ;ah=01h,al=03h — результат скорректирован         

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



AAD


(Ascii Adjust before Division)

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

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

Назначение:

подготовкa двух неупакованных BCD-чисел для операции деления;

преобразование двузначного неупакованного ВCD-числа меньшего 63h (9910) в двоичное представление.


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

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

присвоить регистру al значение (ah*10)+al;

обнулить регистр ah.

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

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

Применение:


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

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


        mov     ah,01h  ;ah=01h         mov     al,08h  ;al=08h => ax=0108h         mov     bl,09   ;bl=09h         aаd             ;al=12h — двоичный эквивалент десятичного числа 18         div     bl      ;al=02h,ah=00h         ог      al,30h  ;al=32h — ASCII-представление числа 2, можно выводить на экран         

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

        mov     ax,3136h        ;ax=3136h         and     ax,0f0fh        ;ax=0106h         aаd             ;al=10h — получили его двоичный эквивалент         

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


AAM


(Ascii Adjust after Multiply)

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

 

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

Назначение:

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

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


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

разделить значение регистра al на 10;

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

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

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

Применение:


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

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

        mov     ah,08h  ;ah=08h         mov     al,09h  ;al= 09h         mul     ah      ;al=48h — двоичный эквивалент 72         aam             ;ah=07h,al=02h         or      ax,3030h        ;ax=3732h — ASCII-представление числа 72

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

;поместим число 60h в регистр ax         mov     ax,60h  ;ax=60h         aаm             ;ax=0906h — получили десятичный эквивалент числа 60h         or      ax,3030h        ;символьный эквивалент, можно выводить на экран

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



AAS


(Ascii Adjust after Substraction)

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

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

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


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


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

уменьшить значение младшего полубайта регистра al на 6;

обнулить значение старшего полубайта регистра al;

установить флаги af и cf в 1;

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

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

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

Применение:


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

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

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

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

        mov     al,05h         mov     bl,08h         sub     al,bl   ;al=0fdh         aas             ;al=07, cf=af=1         or      al,30h  ;al=37h — код символа 7 ;вывод результата на экран         mov     ah,2         mov     dl,al         int     21h         

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



ADC


(Addition with Carry)

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

 

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

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


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

сложить два операнда;

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

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

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

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

Применение:


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

.data sl1     dd      01fe544fh sl2     dd      005044cdh elderREZ        db      0 ;для учета переноса из старшего разряда результата rez     dd      0 .code ...         mov     ax,sl1         add     ax,sl2  ;сложение младших слов слагаемых         mov     rez,ax         mov     ax,sl+2         adc     ax,sl2+2        ;сложение старших слов слагаемых плюс cf         mov     rez+2,ax         adc     elderREZ,0      ;учесть возможный перенос         

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



ADD


(ADDition)

Сложение

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

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


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

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

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

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

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

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

Применение:


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

chiclo  dw      2015 rez     dd      0 ...         add     ax,chislo       ;(ax)=(ax)+ch         mov     word ptr rez,ax         jnc     dop_sum         ;переход, если результат не вышел за разрядную сетку         adc     word ptr rez+2,0        ;расширить результат, для учета переноса                                 ;в старший разряд dop_sum: ...         

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



AND


(logical AND)

Логическое И

 

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

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


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

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

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

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

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

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

Применение:


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


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

u_BCD   db      25h ;упакованное BCD-число s_ch    dw      0 ;место для результата ...         xor     ax,ax   ;очистка ax         mov     al,u_BCD         shl     ax,4    ;ax=0250         mov     al,u_BCD        ;ax=0225 ;преобразование в символьное представление:         and     ax,3f3fh        ;ax=3235h         mov     s_ch,ax         

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



Author


Юров Виктор Иванович

преподаватель СПВУРЭ ПВО

Контактный телефон: (812)141-14-95 (доб. 3-13)



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


К безусловным директивам генерации пользовательской ошибки относится только одна директива — это ERR (.ERR).

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

show macro rg ifdifi ,

goto M_al else ifdifi ,

goto M_ah else .Err endif endif ... endm

Если после определенного таким образом макроопределения в сегменте кода вызвать макрокоманду show с фактическим параметром, отличным от имен регистров ah или al, будет сгенерирована ошибка компиляции (с текстом “User error”), сам процесс компиляции прекращен и, естественно, объектный модуль создан не будет.

Остальные директивы являются условными, так как их поведение определяют некоторые условия.



BOUND


(check array BOUNDs)

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

Схема команды:  bound индекс,границы массива 

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


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


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

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

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

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

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

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

Применение:


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


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

.286    ;это обязательная директива, так как bound         ;входит в систему команд микропроцессоров, начиная с i286 .data BoundMas        label   word Low_Bound       dw      0 Upp_Bound       dw      20 mas     dw      10 dup (?) ...         xor     di,di   ;очистка индексного регистра cycl:         mov     ax,mas[di]      ;перебор элементов массива         add     di,2         bound   di,BoundMas ;если значение в di не будет попадать в границы, то будет вызван ;обработчик прерывания 5, где можно скорректировать ;значение ip/eip в стеке с тем, чтобы выйти ;из бесконечного ;цикла, например, на метку М2 или ;выполнить другие действия         jmp     cycl М2: ...         

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


BSF


(Bit Scan Forward)

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

 

Схема команды:  bsf результат,источник 

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


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

просмотр битов операнда источник, начиная с бита 0 и заканчивая битом 15/31, до тех пор, пока не встретится единичный бит;

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

если единичных битов нет, то флаг zf устанавливается в 1.

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

06
ZF
r

Применение:


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


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

.386         mov     bx,0002h        ;bx=0000 0010b ...         bsf     cx,bx   ;cx=0001h         jz      null         shr     bx,cl   ;bx=0000 0001b ... null:         

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



BSR


(Bit Scan Reverse)

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

Схема команды:  bsr результат,источник 

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


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

просмотр битов операнда источник, начиная со старшего бита 15/31 и заканчивая битом 0, до тех пор, пока не встретится единичный бит;

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

если единичных битов нет, флаг zf устанавливается в 1.

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

06
ZF
r

Применение:


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

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

.386         mov     bx,41h ...         bsr     cx,bx   ;cx=06h         jz      null         shr     bx,ax   ;bx=0001h ... null:...         

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



BSWAP


(Byte SWAP)

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

 

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

Назначение:

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

переход от одной формы адресации к другой.

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


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


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

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

Применение:


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

.486         mov     ebx,1a2c345fhh         bswap   ebx     ;ebx=5f342c1ah         

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



BT


(Bit Test)

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

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

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


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

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

установить флаг cf согласно значению этого бита.

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

00
CF
r

Применение:


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

.386         mov     ebx,01001100h         bt      ebx,8   ;проверка состояния бита 8 и установка cf= в 1         jc      m1      ;перейти на m1, если проверяемый бит равен 1 ...         

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



BTC


(Bit Test and Complement)

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

 

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

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


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

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

инвертировать значение выбранного бита в операнде источник;

установить флаг сf исходным значением бита.

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

00
CF
r

Применение:


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

.386         mov     ebx,01001100h ;проверка состояния бита 8 и его обращение:         btc     ebx,8   ;cf=1 и ebx=01001000h         

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



BTR


(Bit Test and Reset)

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

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

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


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

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

установить флаг cf значением выбранного бита;

установить значение исходного бита в операнде в 0.

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

00
CF
r

Применение:


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

.386         mov     ebx,01001100h ;проверка состояния бита 8 и его сброс в 0         btr     ebx,8   ;cf=1 и ebx=01001000h         

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



BTS


(Bit Test and Set)

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

 

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

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


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

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

установить флаг cf значением выбранного бита;

установить значение исходного бита в операнде источник в 1.

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

00
CF
r

Применение:


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

.386         mov     ebx,01001100h ;проверка состояния бита 0 и его установка в 1         bts     ebx,0   ;cf=0 ebx=01001001h         

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



CALL


(CALL)

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

Схема команды:  call цель 

Назначение:

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

переключение задач.


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


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

метка ближняя — в стек заносится содержимое указателя команд eip/ip и в этот же регистр загружается новое значение адреса, соответствующее метке;

метка дальняя — в стек заносится содержимое указателя команд eip/ip и cs. Затем в эти же регистры загружаются новые значения адресов, соответствующие дальней метке;

r16, 32 или m16, 32 — определяют регистр или ячейку памяти, содержащие смещения в текущем сегменте команд, куда передается управление. При передаче управления в стек заносится содержимое указателя команд eip/ip;

указатель на память — определяет ячейку памяти, содержащую 4 или 6-байтный указатель на вызываемую процедуру. Структура такого указателя 2+2 или 2+4 байта. Интерпретация такого указателя зависит от режима работы микропроцессора:

в реальном режиме — в зависимости от размера адреса (use16 или use32) первые два байта трактуются как сегментный адрес, вторые два/четыре байта, как смещение целевой метки передачи управления. В стеке запоминается содержимое регистров cs и eip/ip;

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

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

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

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


Применение:


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


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



CBW/CWDE


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

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

 

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

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


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


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

анализ знакового бита регистра al:

если знаковый бит al=0, то ah=00h;

если знаковый бит al=1, то ah=0ffh.

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

анализ знакового бита регистра ax:

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

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

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

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

Применение:


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

.386    ;только для cwde, cwd была для i8086         mov     ebx,10fecd23h         mov     ax,-3   ;ax=1111 1111 1111 1101         cwde    ;eax=1111 1111 1111 1111 1111 1111 1111 1101         add     eax,ebx         

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



CDQ


(Convert Double word to Quad word)

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

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

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


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


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

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

Применение:


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

.386 delimoe dd      ... delitel dd      ... ...         mov     eax,delimoe         cdq         idiv    delitel ;частное в eax, остаток в edx         

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



CLC


(CLear Carry flag)

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

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

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


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


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


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

00
CF
0

Применение:


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

        clc             ;cf=0         

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



CLD


(CLear Direction flag)

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

 

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

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


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


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


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

10
DF
0

Применение:


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

        cld             ;df=0         

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


, ,


, ,


,



CLI


(CLear Interrupt flag)

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

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

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


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


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


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

09
IF
0

Применение:


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

        cli             ;if=0         

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



CMC


(CoMplement Carry flag)

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

 

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

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


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


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


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

00
CF
r

Применение:


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

proc1   proc ...         cmc ... proc1   endp ...         call    proc1         jc      m1      ;если cf=1, то переход на m1 ... m1: ...         

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



CMP


(CoMPare operands)

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

Схема команды:  cmp операнд1,операнд2 

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


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

выполнить вычитание (операнд1-операнд2);

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

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

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

Применение:


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

len     equ     10 ...         cmp     ax,len         jne     m1      ;переход если (ax)<>len         jmp     m2      ;переход если (ax)=len         

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



CMPS/CMPSB/CMPSW/CMPSD


(CoMPare String Byte/Word/Double word operands)

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

 

Схема команды:  cmps приемник,источник 
cmpsb 
cmpsw 
cmpsd

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


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

выполнить вычитание элементов (источник - приемник), адреса элементов предварительно должны быть загружены:

адрес источника — в пару регистров ds:esi/si;

адрес назначения — в пару регистров es:edi/di;

в зависимости от состояния флага df изменить значение регистров esi/si и edi/di:

если df=0, то увеличить содержимое этих регистров на длину элемента последовательности;

если df=1, то уменьшить содержимое этих регистров на длину элемента последовательности;

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

если очередные элементы цепочек не равны, то cf=1, zf=0;

если очередные элементы цепочек или цепочки в целом равны, то cf=0, zf=1;

при наличии префикса выполнить определяемые им действия (см. команды repe/repne).

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

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

Применение:


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



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

.data obl1    db      'Строка для сравнения' obl1    db      'Строка для сравнения' a_obl1  dd      obl1 a_obl2  dd      obl2 .code ...         cld             ;просмотр цепочки в направлении возрастания адресов         mov     cx,20   ;длина цепочки         lds     si,a_obl1       ;адрес источника в пару ds:si         les     di,a_obl2       ;адрес назначения в пару ds:si repe    cmpsb           ;сравнивать, пока равны         jnz     m1      ;если не конец цепочки, то встретились разные элементы ...                     ;действия, если цепочки совпали ... m1: ...                     ;действия, если цепочки не совпали         

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


CMPXCHG


(CoMPare and eXCHanGe)

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

Схема команды:  cmpxchg приемник,источник(аккумулятор) 

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


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

выполнить сравнение элементов источник и приемник;

если источник и приемник не равны, то:

установить zf=0;

переслать содержимое операнда приемник в источник (регистр al/ax/eax).

если источник и приемник равны, то:

установить zf=1;

переслать содержимое операнда источник (регистр al/ax/eax) по месту операнда приемник.

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

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

Применение:


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

.486         mov     ax,114eh         mov     bx,8e70h         cmpxchg bx,ax         jz      m1      ;переход, если zf=1, то есть операнды равны                         ;и ax не изменился ...                     ;действия, если операнды не равны m1:         

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



CWD


(Convert Word to Double word)

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

 

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

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


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


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

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

Применение:


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

        mov     ax,25 ...         mov     bx,4         cwd         div     bx         

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



DAA


(Decimal Adjust for Addition)

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

 

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

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


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


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

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

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

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

для ситуации 1 содержимое регистра al увеличивается на 6;

для ситуации 2 содержимое регистра al увеличивается на 60h;

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

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

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

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

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

Применение:


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

        mov     al,69h  ;69h — упакованное BCD-число         mov     bl,74h  ;74h — упакованное BCD-число         adc     al,bl   ;al=0ddh         daa             ;cf=1, al=43h ;если перенос, то переход на ту ветвь программы, ;где он будет учтен:         jc m1         

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



DAS


(Decimal Adjust for Subtraction)

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

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

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


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


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

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

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

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

для ситуации 1 содержимое регистра al уменьшается на 6;

для ситуации 2 содержимое регистра al уменьшается на 60h;

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

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

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

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

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

Применение:


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

        mov     ah,08h  ;ah=08h         mov     al,05h  ;al=05h         add     al,ah   ;al=al+ah=05h+08h=0dh — не BCD-число         xor     ah,ah   ;ah=0         aaa             ;ah=01h,al=03h — результат скорректирован         

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



DEC


(DECrement operand by 1)

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

 

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

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


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


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

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

Применение:


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

        mov     al,9 ...         dec     al      ;al=8         

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



Директива IRP


Директива IRP имеет следующий синтаксис:

IRP формальный_аргумент,

последовательность_строк ENDM

Действие данной директивы заключается в том, что она повторяет последовательность_строк N раз, то есть столько раз, сколько строк_символов заключено в угловые скобки во втором операнде директивы IRP. Но это еще не все.
Повторение последовательности_строк

сопровождается заменой в ней формального_аргумента строкой символов из второго операнда.
Так, при первой генерации последовательности_строк

формальный_аргумент в них заменяется на строка_символов_1.
Если есть строка_символов_2, то это приводит к генерации второй копии последовательности_строк, в которой формальный_аргумент заменяется на строка_символов_2. Эти действия продолжаются до строка_символов_N включительно.

К примеру, рассмотрим результат определения в программе следующей конструкции:

irp ini,

db ini endm

Макрогенератором будет сгенерировано следующее макрорасширение:

db 1 db 2 db 3 db 4 db 5



Директива IRPC


Директива IRPC имеет следующий синтаксис:

IRPC формальный_аргумент,строка_символов последовательность строк ENDM

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

будет определяться количеством символов в строка_символов.
К примеру:

irpc rg,

push rg&x endm

В процессе макрогенерации эта директива развернется в следующую последовательность строк:

push ax push bx push cx push dx



Директивы .ERRB (ERRIFB) и .ERRNB (ERRIFNB)


Синтаксис директив:

.ERRB (ERRIFB) <имя_формального_аргумента> — генерация пользовательской ошибки, если <имя_формального_аргумента>

пропущено;

.ERRNB (ERRIFNB) <имя_формального_аргумента> — генерация пользовательской ошибки, если <имя_формального_аргумента>

присутствует.

Данные директивы применяются для генерации ошибки трансляции в зависимости от того, задан или нет при вызове макрокоманды фактический аргумент, соответствующий формальному аргументу в заголовке макроопределения с именем <имя_формального_аргумента>.
По принципу действия эти директивы полностью аналогичны соответствующим директивам условной компиляции IFB и IFNB. Их обычно используют для проверки задания параметров при вызове макроса.

Строка имя_формального_аргумента должна быть заключена в угловые скобки.

К примеру, определим обязательность задания фактического аргумента, соответствующего формальному аргументу rg, в макросе show:

show macro rg ;если rg в макрокоманде не будет задан, ;то завершить компиляцию .errb

;текст макроопределения ;... endm



Директивы .ERRDEF (ERRIFDEF) и .ERRNDEF (ERRIFNDEF)


Синтаксис директив:

.ERRDEF (ERRIFDEF) символическое_имя — если указанное имя определено до выдачи этой директивы в программе, то генерируется пользовательская ошибка.

.ERRNDEF(ERRIFNDEF) символическое_имя — если указанное символическое_имя не определено до момента обработки транслятором данной директивы, то генерируется пользовательская ошибка.

Данные директивы генерируют ошибку трансляции в зависимости от того, определено или нет некоторое символическое_имя в программе.
Не забывайте о том, что компилятор TASM по умолчанию формирует объектный модуль за один проход исходного текста программы. Следовательно, директивы .ERRDEF (ERRIFDEF)

и .ERRNDEF (ERRIFNDEF) отслеживают факт определения символического_имени только в той части исходного текста, которая находится до этих директив.



Директивы .ERRDIF (ERRIFDIF) и .ERRIDN (ERRIFIDN)


Синтаксис директив:

.ERRDIF (ERRIFDIF) <строка_1>,<строка_2> — директива, генерирующая пользовательскую ошибку, если две строки посимвольно не совпадают. Строки могут быть символическими именами, числами или выражениями и должны быть заключены в угловые скобки. Аналогично директиве условной компиляции IFDIF, при сравнении учитывается различие прописных и строчных букв.

.ERRIDN (ERRIFIDN) <строка_1>,<строка_2> — директива, генерирующая пользовательскую ошибку, если строки посимвольно идентичны. Строчное и прописное написание одной и той же буквы воспринимается как разные символы.

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

ERRIFDIFI <строка_1>,<строка_2> — то же, что и ERRIFDIF, но игнорируется различие строчных и прописных букв при сравнении <строка_1> и <строка_2>.

ERRIFIDNI <строка_1>,<строка_2> — то же, что и ERRIFIDN, но игнорируется различие строчных и прописных букв при сравнении <строка_1> и <строка_2>.

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



Директивы .ERRE (ERRIFE) и .ERRNZ (ERRIF)


Синтаксис директив:

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

.ERRNZ(ERRIF) константное_выражение — директива вызывает пользовательскую ошибку, если константное_выражение истинно (не равно нулю). Вычисление константного_выражения должно приводить к абсолютному значению и не может содержать компонентов, являющихся ссылками вперед.



Директивы генерации ошибок


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

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

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

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



Директивы IF и IFE


Синтаксис этих директив следующий:

IF(E) логическое_выражение фрагмент_программы_1 ELSE фрагмент_программы_2 ENDIF

Обработка этих директив макроассемблером заключается в вычислении логического_выражения и включении в объектный модуль фрагмент_программы_1 или фрагмент_программы_2 в зависимости от того, в какой директиве IF или IFE это выражение встретилось:

если в директиве IF логическое выражение истинно, то в объектный модуль помещается фрагмент_программы_1.
Если логическое выражение ложно, то при наличии директивы ELSE в объектный код помещается фрагмент_программы_2. Если же директивы ELSE нет, то вся часть программы между директивами IF и ENDIF игнорируется и в объектный модуль ничего не включается. Кстати сказать, понятие истинности и ложности

значения логического_выражения весьма условно. Ложным оно будет считаться, если его значение равно нулю, а истинным — при любом значении, отличном от нуля. директива IFE аналогично директиве IF анализирует значение логического_выражения. Но теперь для включения фрагмент_программы_1 в объектный модуль требуется, чтобы логическое_выражение имело значение “ложь”.

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

К примеру, составим макрос для определения в программе области памяти длиной не более 50 и не менее 10 байт (листинг 5).

Листинг 5. Использование условных директив IF и IFE ;prg_13_4.asm masm model small stack 256 def_tab_50 macro len if len GE 50 GOTO exit endif if len LT 10 :exit EXITM endif rept len db 0 endm endm .data def_tab_50 15 def_tab_50 5 .code main: mov ax,@data mov ds,ax exit: mov ax,4c00h int 21h end main ENDIF

Введите и оттранслируйте листинг 5. При этом не забывайте о том, что условные директивы действуют на шаге трансляции, и поэтому результат их работы можно увидеть только после макрогенерации, то есть в листинге программы.
В нем вы увидите, что в результате трансляции строка 18 листинга 5 развернется в пятнадцать нулевых байт, а строка 19 оставит макрогенератор совершенно равнодушным, так как значение фактического операнда в строках 6 и 9 будет ложным. Обратите внимание, что для обработки реакции на ложный результат анализа в условной директиве мы использовали макродирективы EXITM и GOTO.

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


и IFEотладочная печать.
Суть здесь в том, что в процессе отладки программы почти всегда возникает необходимость динамически отслеживать состояние определенных программно- аппаратных объектов, в качестве которых могут выступать переменные, регистры микропроцессора и т. п. После этапа отладки отпадает необходимость в таких диагностических сообщениях. Для их устранения нужно корректировать исходный текст программы, после чего ее следует подвергнуть повторной трансляции. Но есть более изящный выход.
Можно определить в программе некоторую переменную, к примеру debug, и использовать ее совместно с условными директивами IF или IFE. К примеру,
... debug equ 1 ... .code ... if debug ;любые команды и директивы ассемблера ;(вывод на печать или монитор) endif

На время отладки и тестирования программы вы можете заключить отдельные участки кода в своеобразные операторные скобки в виде директив IF и ENDIF (строки 6-9 последнего фрагмента), которые реагируют на значение логической переменной debug. При значении debug = 0 транслятор полностью проигнорирует текст внутри этих условных операторных скобок; при debug = 1, наоборот, будут выполнены все действия, описанные внутри них.




Директивы IFB и IFNB


Синтаксис этих директив следующий:

IF(N)B аргумент фрагмент_программы_1 ELSE фрагмент_программы_2 ENDIF

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

Директива IFB проверяет равенство аргумента пробелу. В качестве аргумента могут выступать имя или число.
Если его значение равно пробелу (то есть фактический аргумент при вызове макрокоманды не был задан), то транслируется и помещается в объектный модуль фрагмент_программы_1.
В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент_программы_1. Если же директивы ELSE нет, то при равенстве аргумента пробелу вся часть программы между директивами IFB и ENDIF игнорируется и в объектный модуль не включается. Действие IFNB обратно IFB. Если значение аргумента в программе не равно пробелу, то транслируется фрагмент_программы_1.
В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент_программы_1. Если же директивы ELSE нет, то вся часть программы (при неравенстве аргумента пробелу) между директивами IFNB и ENDIF игнорируется и в объектный модуль не включается.

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

show macro reg ifb

display 'не задан регистр' exitm endif ... endm

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



Директивы IFDEF и IFNDEF


Синтаксис этих директив следующий:

IF(N)DEF символическое_имя фрагмент_программы_1 ELSE фрагмент_программы_2 ENDIF

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

проверяет, описано или нет в программе символическое_имя, и если это так, то в объектный модуль помещается фрагмент_программы_1. В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент_программы_2.
Если же директивы ELSE нет (и символическое_имя в программе не описано), то вся часть программы между директивами IF и ENDIF игнорируется и в объектный модуль не включается.

Действие IFNDEF обратно IFDEF. Если символического_имени в программе нет, то транслируется фрагмент_программы_1. Если оно присутствует, то при наличии ELSE транслируется фрагмент_программы_2. Если ELSE отсутствует, а символическое_имя в программе определено, то часть программы, заключенная между IFNDEF и ENDIF, игнорируется.

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

если switch = 0, то сгенерировать фрагмент для вычисления выражения
y = x*2**n; если switch = 1, то сгенерировать фрагмент для вычисления выражения
y = x/2**n; если switch не определен, то ничего не генерировать.

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

ifndef sw ;если sw не определено, то выйти из макроса EXITM else ;иначе — на вычисление mov cl,n ife sw sal x,cl ;умножение на степень 2 сдвигом влево else sar x,cl ;деление на степень 2 сдвигом вправо endif endif

Как видим, эти директивы логически связаны с директивами IF

и IFE, то есть их можно применять в тех же самых случаях, что и последние.
Есть еще одна интересная возможность использования этих директив. На уроке 4 мы обсуждали формат командной строки и говорили об опциях, которые в ней можно задавать. Вспомните одну из опций командной строки TASM — опцию
/dидентификатор=значение.
Ее использование дает возможность управлять значением идентификатора прямо из командной строки транслятора, не изменяя при этом текста программы.
В качестве примера рассмотрим листинг 6, в котором мы попытаемся с помощью макроса контролировать процесс резервирования и инициализации некоторой области памяти в сегменте данных.

Листинг 6. Инициализация значения идентификатора из командной строки ;prg_13_5.asm masm model small stack 256 def_tab_50 macro len ifndef len display 'size_m не определено, задайте значение 10exitm else if len GE 50 GOTO exit endif if len LT 10 :exit EXITM endif rept len db 0 endm endif endm ;size_m=15 .data def_tab_50 size_m

.code main: mov ax,@data mov ds,ax exit: mov ax,4c00h int 21h end main

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

Определите где-то в начале исходного текста программы значение этой переменной с помощью equ:

size_m equ 15

Запустите программу на трансляцию командной строкой вида
tasm /dsize_m=15 /zi prg_13_2,,,

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



Директивы IFIDN, IFIDNI, IFDIF и IFDIFI


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

Синтаксис этих директив:

IFIDN(I) аргумент_1,аргумент_2 фрагмент_программы_1 ELSE фрагмент_программы_2 ENDIF

IFDIF(I) аргумент_1,аргумент_2 фрагмент_программы_1 ELSE фрагмент_программы_2 ENDIF

В этих директивах проверяются аргумент_1 и аргумент_2 как строки символов. Какой именно код — фрагмент_программы_1 или фрагмент_программы_1 — будет транслироваться по результатам сравнения, зависит от кода директивы.
Парность этих директив объясняется тем, что они позволяют учитывать либо не учитывать различие строчных и прописных букв. Так, директивы IFIDNI и IFDIFI игнорируют это различие, а IFIDN и IFDIF — учитывают.

Директива IFIDN(I) сравнивает символьные значения аргумент_1 и аргумент_2.
Если результат сравнения положительный, то фрагмент_программы_1 транслируется и помещается в объектный модуль.
В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент_программы_1.
Если же директивы ELSE нет, то вся часть программы между директивами IFIDN(I) и ENDIF игнорируется и в объектный модуль не включается.

Действие IFDIF(I) обратно IFIDN(I).
Если результат сравнения отрицательный (строки не совпадают), транслируется фрагмент_программы_1.
В противном случае все происходит аналогично рассмотренным ранее директивам.

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

show macro rg ifdifi ,

goto M_al else ifdifi ,

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



Директивы изменения формата листинга


Директивы этой группы позволяют управлять форматом файла листинга.

.PAGE


Директива .PAGE задает высоту и ширину страницы файла листинга и начинает его новую страницу. Она имеет следующий синтаксис:

PAGE [число_строк][,число_столбцов] PAGE +

Здесь:

число_строк задает число строк, выводимых на странице листинга;

число столбцов находится в диапазоне 59...255 и задает число столбцов на странице.

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

можно разбивать листинг на разделы, в пределах которых нумерация начинается с нуля. Так, при указании после директивы .PAGE символа “+” начинается новая страница, номер раздела увеличивается, а номер страницы снова устанавливается в 1. Если использовать директиву .PAGE без аргументов, то листинг возобновляется с новой страницы без изменения номера раздела.

%PAGESIZE (.PAGESIZE)


Директива %PAGESIZE работает так же, как и директива .PAGE, но, в отличие от последней, она не начинает новую страницу, а лишь определяет ее параметры:

%PAGESIZE [число_строк][,число_столбцов]

%NEWPAGE


Директива %NEWPAGE работает аналогично директиве .PAGE без аргументов. Строки исходного текста после директивы %NEWPAGE

будут начинаться с новой страницы.

%BIN


Директива %BIN устанавливает длину поля объектного кода в файле листинга. Ее синтаксис:

%BIN размер

Здесь размер — некоторая константа.


По умолчанию поле объектного кода занимает в файле листинга до 20 позиций.

%DEPTH


Директива %DEPTH устанавливает размер поля глубины в файле листинга. Ее синтаксис:

%DEPTH размер

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


Напомню, что данное поле показывает уровень вложенности включаемых файлов (INCLUDE) и макрорасширений. Если указать в качестве размера значение 0, то поле уровня вложенности не выводится. По умолчанию это поле имеет значение 1.

%LINENUM



Директива %LINENUM позволяет задать размер поля занимаемого номерами строк в файле листинга:

%LINENUM размер

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

%TRUNC и %NOTRUNC

Директивы %TRUNC и %NOTRUNC предназначены для усечения длинных полей листинга. Их синтаксис:

%TRUNC и %NOTRUNC

Если некоторая строка исходного кода получается слишком длинной, то она автоматически усекается. Если возникает необходимость увидеть всю генерируемую строку, то можно использовать директиву %NOTRUNC, действие которой будет заключаться в том, что слишком длинная строка будет переноситься на следующую строку. Для включения режима усечения нужно использовать директиву %TRUNC. Такие переключения можно осуществлять неограниченное количество раз.

%PCNT

Директива %PCNT задает размер поля “сегмент:смещение” в файле листинга. Ее синтаксис:

%PCNT размер

Здесь размер — число столбцов, которое необходимо отвести для смещения в текущем ассемблируемом сегменте.

По умолчанию TASM устанавливает размер, равный 4 для обычных 16-битных сегментов (атрибут размера адреса use16) и 8 для 32-битных сегментов (атрибут размера адреса use32). Директива %PCNT позволяет переопределить эти используемые по умолчанию значения.

%TITLE

Директива %TITLE задает заголовок файла листинга. Ее синтаксис:

%TITLE “текст”

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

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

%SUBTTL

Директива %SUBTTL задает подзаголовок файла листинга. Ее синтаксис:

%SUBTTL “текст”

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

%TABSIZE

Директива %TABSIZE задает позицию табуляции в файле листинга. Ее синтаксис:

%TABSIZE размер

Здесь размер — число столбцов между двумя позициями табуляции в файле листинга (по умолчанию 8 столбцов).

%TEXT

Директива %TEXT используется для задания длины поля исходного текста в файле листинга. Ее синтаксис:

%TEXT размер

Здесь размер — число столбцов, используемых для вывода исходных строк. Если размер строки превышает длину этого поля, то строка будет либо усекаться, либо переноситься на следующую строку, в зависимости от директив %TRUNC или %NOTRUNC. 


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


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

Введение в язык ассемблера этих директив значительно повышает его мощь.
Всего имеется 10 типов условных директив компиляции. Их логично попарно объединить в четыре группы:

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

Условные директивы компиляции имеют общий синтаксис и применяются в составе следующей синтаксической конструкции:

IFxxx логическое_выражение_или_аргументы фрагмент_программы_1 ELSE фрагмент_программы_2 ENDIF

Заключение некоторых фрагментов текста программы — фрагмент_программы_1 и фрагмент_программы_2 — между директивами IFxxx, ELSE и ENDIF приводит к их выборочному включению в объектный модуль. Какой именно из этих фрагментов — фрагмент_программы_1 или фрагмент_программы_2 — будет включен в объектный модуль, зависит от конкретного типа условной директивы, задаваемого значением xxx, и значения условия, определяемого операндом (операндами) условной директивы логическое_выражение_или_аргумент(ы).

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



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


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

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

с одним сегментом кода;

с одним сегментом стека;

с одним сегментом данных;

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

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

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

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

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


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

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

BYTE — выравнивание не выполняется. Сегмент может начинаться с любого адреса памяти;

WORD — сегмент начинается по адресу, кратному двум, то есть последний (младший) значащий бит физического адреса равен 0 (выравнивание на границу слова);

DWORD — сегмент начинается по адресу, кратному четырем, то есть два последних (младших) значащих бита равны 0 (выравнивание на границу двойного слова);


PARA — сегмент начинается по адресу, кратному 16, то есть последняя шестнадцатеричная цифра адреса должна быть 0h (выравнивание на границу параграфа);

PAGE — сегмент начинается по адресу, кратному 256, то есть две последние шестнадцатеричные цифры должны быть 00h (выравнивание на границу 256-байтной страницы);

MEMPAGE — сегмент начинается по адресу, кратному 4 Кбайт, то есть три последние шестнадцатеричные цифры должны быть 000h (адрес следующей 4-Кбайтной страницы памяти).

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

 

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

PRIVATE — сегмент не будет объединяться с другими сегментами с тем же именем вне данного модуля;

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

COMMON — располагает все сегменты с одним и тем же именем по одному адресу. Все сегменты с данным именем будут перекрываться и совместно использовать память. Размер полученного в результате сегмента будет равен размеру самого большого сегмента;

AT xxxx — располагает сегмент по абсолютному адресу параграфа (параграф — объем памяти, кратный 16; поэтому последняя шестнадцатеричная цифра адреса параграфа равна 0). Абсолютный адрес параграфа задается выражением xxx. Компоновщик располагает сегмент по заданному адресу памяти (это можно использовать, например, для доступа к видеопамяти или области ПЗУ), учитывая атрибут комбинирования. Физически это означает, что сегмент при загрузке в память будет расположен, начиная с этого абсолютного адреса параграфа, но для доступа к нему в соответствующий сегментный регистр должно быть загружено заданное в атрибуте значение. Все метки и адреса в определенном таким образом сегменте отсчитываются относительно заданного абсолютного адреса;



STACK — определение сегмента стека. Заставляет компоновщик соединить все одноименные сегменты и вычислять адреса в этих сегментах относительно регистра ss. Комбинированный тип STACK (стек) аналогичен комбинированному типу PUBLIC, за исключением того, что регистр ss является стандартным сегментным регистром для сегментов стека. Регистр sp устанавливается на конец объединенного сегмента стека. Если не указано ни одного сегмента стека, компоновщик выдаст предупреждение, что стековый сегмент не найден. Если сегмент стека создан, а комбинированный тип STACK не используется, программист должен явно загрузить в регистр ss адрес сегмента (подобно тому, как это делается для регистра ds).

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

 

Атрибут класса сегмента (тип класса) — это заключенная в кавычки строка, помогающая компоновщику определить соответствующий порядок следования сегментов при собирании программы из сегментов нескольких модулей. Компоновщик объединяет вместе в памяти все сегменты с одним и тем же именем класса (имя класса, в общем случае, может быть любым, но лучше, если оно будет отражать функциональное назначение сегмента). Типичным примером использования имени класса является объединение в группу всех сегментов кода программы (обычно для этого используется класс “code”). С помощью механизма типизации класса можно группировать также сегменты инициализированных и неинициализированных данных;

Атрибут размера сегмента. Для процессоров i80386 и выше сегменты могут быть 16 или 32-разрядными. Это влияет, прежде всего, на размер сегмента и порядок формирования физического адреса внутри него. Атрибут может принимать следующие значения:

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

USE32 — сегмент будет 32-разрядным. При формирования физического адреса может использоваться 32-разрядное смещение. Поэтому такой сегмент может содержать до 4 Гбайт кода или данных.



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

можно использовать вместо аргумента имя сегмента; в этом случае будет выборочно разрываться связь между сегментом с именем имя сегмента и соответствующим сегментным регистром (см. рис. 15).



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

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

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



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

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

          Листинг 1. Использование упрощенных директив сегментации ;---------Prg_3_1.asm------------------------------- masm                    ;режим работы TASM: ideal или masm model   small           ;модель памяти .data                   ;сегмент данных message db      'Введите две шестнадцатеричные цифры,$' .stack                  ;сегмент стека         db      256     dup ('?')       ;сегмент стека .code                   ;сегмент кода main    proc            ;начало процедуры main         mov     ax,@data        ;заносим адрес сегмента данных в регистр ax         mov     ds,ax   ;ax в ds ;далее текст программы (см. сегмента кода в листинге 3.1 книги)         mov     ax,4c00h        ;пересылка 4c00h в регистр ax         int     21h             ;вызов прерывания с номером 21h main    endp            ;конец процедуры main end     main            ;конец программы с точкой входа main

<


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



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

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



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


Формат директивы 
(режим MASM)
Формат директивы 
(режим IDEAL)
Назначение
.CODE [имя] CODESEG[имя] Начало или продолжение сегмента кода
.DATA DATASEG Начало или продолжение сегмента инициализированных данных. Также используется для определения данных типа near
.CONST CONST Начало или продолжение сегмента постоянных данных (констант) модуля
.DATA? UDATASEG Начало или продолжение сегмента неинициализированных данных. Также используется для определения данных типа near
.STACK [размер] STACK [размер] Начало или продолжение сегмента стека модуля. Параметр [размер] задает размер стека
.FARDATA [имя] FARDATA [имя] Начало или продолжение сегмента инициализированных данных типа far
.FARDATA? [имя] UFARDATA [имя] Начало или продолжение сегмента неинициализированных данных типа far
Наличие в некоторых директивах параметра [имя] говорит о том, что возможно определение нескольких сегментов этого типа. С другой стороны, наличие нескольких видов сегментов данных обусловлено требованием обеспечить совместимость с некоторыми компиляторами языков высокого уровня, которые создают разные сегменты данных для инициализированных и неинициализированных данных, а также констант.

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





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


Имя идентификатора Значение переменной
@code Физический адрес сегмента кода
@data Физический адрес сегмента данных типа near
@fardata Физический адрес сегмента данных типа far
@fardata? Физический адрес сегмента неинициализированных данных типа far
@curseg Физический адрес сегмента неинициализированных данных типа far
@stack Физический адрес сегмента стека
Если вы посмотрите на текст , то увидите пример использования одного из этих идентификаторов. Это @data; с его помощью мы получили значение физического адреса сегмента данных нашей программы.

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



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


Модель Тип кода Тип данных Назначение модели
TINY near near Код и данные объединены в одну группу с именем DGROUP. 
Используется для создания программ формата .com.
SMALL near near Код занимает один сегмент, данные объединены в одну группу с именем DGROUP. 
Эту модель обычно используют для большинства программ на ассемблере
MEDIUM far near Код занимает несколько сегментов, по одному на каждый объединяемый программный модуль. 
Все ссылки на передачу управления — типа far. 
Данные объединены в одной группе; все ссылки на них — типа near
COMPACT near far Код в одном сегменте; 
ссылка на данные — типа far
LARGE far far Код в нескольких сегментах, по одному на каждый объединяемый программный модуль
Параметр модификатор директивы MODEL позволяет уточнить некоторые особенности использования выбранной модели памяти (табл. 6).



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


Значение модификатора Назначение
use16 Сегменты выбранной модели используются как 16-битные (если соответствующей директивой указан процессор i80386 или i80486)
use32 Сегменты выбранной модели используются как 32-битные (если соответствующей директивой указан процессор i80386 или i80486)
dos Программа будет работать в MS-DOS
Необязательные параметры язык и модификатор языка определяют некоторые особенности вызова процедур. Необходимость в использовании этих параметров появляется при написании и связывании программ на различных языках программирования.

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

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


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


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

При рассмотрении директив обращайте внимание на то, что их формат отличается для режимов работы транслятора MASM и IDEAL:

директивам режима MASM предшествует точка;

директивам режима IDEAL предшествует знак “%”.

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



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


Последний тип макросредств — директивы условной компиляции.
Существует два типа этих директив:

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

С этими директивами применяются директивы управления процессом генерации макрорасширений EXITM и GOTO.

Директива EXITM не имеет операндов, и ее действие заключается в том, что она немедленно прекращает процесс генерации макрорасширения, начиная с того места, где она встретилась в макроопределении.

Директива GOTO имя_метки переводит процесс генерации макроопределения в другое место, прекращая тем самым последовательное разворачивание строк макроопределения. Метка, на которую передается управление, имеет специальный формат:

:имя_метки

Примеры применения этих директив будут приведены ниже.



Директивы вывода блоков условного ассемблирования


%CONDS и %NOCONDS (.LFCOND и .SFCONDS)


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


Директива %CONDS (.LFCOND) заставляет TASM выводить в файл листинга все операторы условных блоков. При этом в файл листинга выводятся все блоки, в том числе с условием false. Директива %NOCONDS (.SFCONDS) запрещает вывод в файл листинга блоков условного ассемблирования с условием false.


Директива .TFCOND переключает режимы вывода %CONDS (.LFCOND) и %NOCONDS (.SFCONDS). Эту директиву можно использовать как отдельно, так и совместно с директивами .LFCOND и .SFCONDS.


Первая директива .TFCOND, которую обнаруживает TASM, разрешает вывод в листинг всех блоков условного ассемблирования.


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



Директивы вывода листинга перекрестных ссылок


Приведенные выше директивы %SYMS и %NOSYMS

регулировали вывод в листинг таблицы идентификаторов, в которой приводится информация о метках, группах и сегментах, но там не сообщается, где они определены и где используются. Информация в таблице перекрестных ссылок исправляет этот недостаток. Она облегчает поиск меток и полезна для отладки программы. В приложении 1 приведена опция командной строки TASM /c для получения таблицы перекрестных ссылок. Но действие этой опции распространяется на весь исходный файл, что может быть не совсем удобным. Поэтому TASM дополнительно предоставляет директивы для создания таблиц перекрестных ссылок только для отдельных частей исходного кода. Директивы %CREF (.CREF) и %NOCREF (.XCREF) соответственно разрешают и запрещают сбор информации о перекрестных ссылках, начиная с точки, где они были определены. При этом директивы %NOCREF (.XCREF) позволяют выборочно запрещать сбор информации о перекрестных ссылках для определенных идентификаторов в программе. Эти директивы имеют следующий синтаксис:

%NOCREF (.XCREF) [идентификатор, ...]

Если в директиве %NOCREF (.XCREF) не указать идентификатор, то вывод перекрестных ссылок запрещается полностью, если указать некоторые идентификаторы, то информация не будет собираться только для этих идентификаторов.



Директивы вывода макрорасширений


%MACS (.LALL) и %NOMACS (.SALL)


Аналогично директивам вывода блоков условной компиляции при отладке программы удобно регулировать полноту информации о применяемых макрокомандах. По умолчанию транслятор включает макрорасширения в файл листинга. Можно запретить вывод макрорасширений в файл листинга, что удобно на некоторых стадиях отладки. Директива %MACS (.LALL) разрешает вывод в листинг всех макрорасширений. Директивы %NOMACS (.SALL)

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



Директивы вывода текста включаемых файлов


%INCL и %NOINCL


Эти директивы позволяют регулировать включение в файл листинга текста включаемых файлов (по директиве INCLUDE). По умолчанию включаемые файлы записываются в файл листинга. Директива %NOINCL

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



Директивы WHILE и REPT


Директивы WHILE и REPT применяют для повторения определенное количество раз некоторой последовательности строк.
Эти директивы имеют следующий синтаксис:

WHILE константное_выражение последовательность_строк ENDM

REPT константное_выражение последовательность строк ENDM

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

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

должно подвергаться изменению внутри последовательность_строк

в процессе макрогенерации).

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

В качестве примера рассмотрим листинг 4, в котором демонстрируется применение директив WHILE и REPT для резервирования области памяти в сегменте данных. Имя идентификатора и длина области задаются в качестве параметров для соответствующих макросов def_sto_1 и def_sto_2.

Листинг 4. Использование директив повторения ;prg_13_3.asm def_sto_1 macro id_table,ln:=

;макрос резервирования памяти длиной len. ;Используется WHILE id_table label byte len=ln while len db 0 len=len-1 endm endm def_sto_2 macro id_table,len ;макрос резервирования памяти длиной len id_table label byte rept len db 0 endm endm

data segment para public 'data' def_sto_1 tab_1,10 def_sto_2 tab_2,10 data ends ;сегменты команд и стека в этой программе необязательны end

Заметьте, что счетчик повторений в директиве REPT

уменьшается автоматически после каждой итерации цикла. Проанализируйте результат трансляции листинга 13.3.

Таким образом, директивы REPT и WHILE удобно применять для “размножения” в тексте программы последовательности одинаковых строк без внесения в эти строки каких-либо изменений.

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



DIV


(DIVide unsigned)

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

Схема команды:  div делитель 

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


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


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

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

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

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

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

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

Применение:


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

        mov     ax,10234         mov     bl,154         div     bl      ;ah=остаток, al=частное         

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



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


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

31 18 17 16 15 14 1312 11 10 09 08 07 06 05 04 03 02 01 00
0 0 VM RF 0 NT IOPL OF DF IF TF SF ZF 0 AF 0 PF 1 CF
                                   

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

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

0 — после выполнения команды флаг сбрасывается (равен 0);

r — значение флага зависит от результата работы команды;

? — после выполнения команды флаг не определен;

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

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

r8, r16, r32 — операнд в одном из регистров размером байт, слово или двойное слово;

m8, m16, m32, m48 — операнд в памяти размером байт, слово, двойное слово или 48 бит;

i8, i16, i32 — непосредственный операнд размером байт, слово или двойное слово;

a8, a16, a32 — относительный адрес (смещение) в сегменте кода.

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

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

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



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


TASM предоставляет средства для вывода текстового сообщения во время трансляции программы — директивы DISPLAY и %OUT. С их помощью можно, при необходимости, следить за ходом трансляции.
К примеру:

display недопустимые аргументы макрокоманды ... %out недопустимое имя регистра

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