1. ↑ 12 Переменный размер блока относится к системам, поддерживающим переменную длину блока пофайлово. (Аналогично extent, но это несколько иная реализация.) В настоящий момент UFS2 поддерживает такой вариант в режиме только для чтения.
2. ↑ 123 DoubleSpace в DOS 6, и DriveSpace в Windows 95 иWindows 98 предоставлял схемы сжатия данных для FAT; в данное время не поддерживается Microsoft.
3. ↑ 1234 Так же поддерживаются и другие варианты соотношений блок: фрагмент; Большинство реализаций рекомендуют соотношение 8:1.
4. ↑ e2compr, набор патчей реализующих поблочное сжатиедля ext2, доступен с 1997, но его никогда не включали в основное ядро Linux.
5. ↑ 12 В ext2 и ext3 предполагалась поддержка фрагментов, однако она так и не была реализована.
6. ↑ Reiser4 включает сжатие данных, но это не представлено в VFS API. Reiser4 поддерживает прозрачное сжатие и шифрованиес плагином cryptcompress который идёт в комплекте с версией 4.1.
7. ↑ В «расширенном» режиме.
8. ↑ Подвыделение блоков делит устройство хранения на блоки от 4 KiB до 64 KiB (обычно 8 KiB), и если блок не используется целиком, остаток снова делится на 512-байтные подблоки для других файлов, обычно небольшого размера.
9. ↑ Присутствует в зависимости от реализации UDF.
10.
Операторы ассемблера.
Оператор – это некоторая операция, связывающая набор чисел, меток или строк, выполняющая свои действия в процессе ассемблирования.
Выражение — это набор чисел, меток или строк, связанных друг с другом операторами.
Например: 2 + 2 — выражение, состоящее из двух чисел (2 и 2) и оператора +. Каждое выражение имеет значение, которое определяется как результат действия операторов. Так, значение выражения 2 + 2 — число 4.
Оператор <> (угловые скобки). Часть выражения, заключенная в угловые скобки, не вычисляется, а применяется как строка символов, например:
message1 equ <foobar>
Оператор () (круглые скобки). Часть выражения, заключенная в круглые скобки, вычисляется в первую очередь. mov al, 2*(3+4) ; mov al,14
Арифметические операторы: + (плюс), – (минус), * (умножение), / (целочисленное деление), MOD (остаток от деления). Они выполняют соответствующие арифметические действия. mov al,90 mod 7 ; mov al,6
Кроме того, к арифметическим операторам относится унарный минус — минус, который ставят перед отрицательным числом.
Логические операторы: AND (И), NOT (НЕ), OR (ИЛИ), XOR (исключающее ИЛИ), SHL (сдвиг влево), SHR (сдвиг вправо). Эти операторы выполняют соответствующие логические действия.
mov ax,1234h AND 4321h ; mov ax,0220h
Операторы сравнения: EQ (равно), GE (больше или равно), GT (больше), LE (меньше или равно), LT (меньше), NE (не равно). Результат действия каждого из этих операторов — единица, если условие выполняется, и ноль — если не выполняется. .errnz $ gt 65535 ; Если адрес больше 64 Кб – ошибка
Операторы адресации:
SEG выражение — сегментный адрес;
OFFSET выражение — смещение;
THIS тип — текущий адрес (MASM и TASM);
Тип PTR выражение — переопределение типа;
LARGE выражение — 32-битное смещение (TASM и WASM);
SMALL выражение — 16-битное смещение (TASM и WASM);
SHORT выражение — 8-битное смещение.
SEG и OFFSET возвращают соответствующую часть адреса своего аргумента:
LARGE, SMALL и SHORT используются с командами передачи управления, если возникают двусмысленности при косвенных переходах:
jmp large dword ptr old_address ; Переменная old_address содержит 32-битное смещениеjmp small dword ptr old_address ; Переменная old_address содержит 16-битный сегментный адрес и 16-битное смещение.jmp short short_label ; Метка short_label находится ближе, чем +128/-127 байт от этой команды, так что можно использовать короткую форму команды JMP.
Другие операторы:
. (точка) — ссылка на элемент структуры;
: (двоеточие) — переопределение сегмента;
[] (прямые скобки) — косвенная адресация;
? — неинициализированное значение;
число DUP (значение) — повторяющееся значение.
LENGTH метка – число элементов данныхtable dw 0,1,2,3,4,5,6,7 ; Определить таблицу из 8 слов.table_count = length table ; table_count = 8SIZE метка – размер данныхtable_size = size table ; table_size = 16
Отличия операторов от команд ассемблера.
Операторы получают результат вычисления в ходе ассемблирования программы, следовательно, в полученном коде используются только значения, в отличие от команд, которые непосредственно выполняются в ходе программы.
11.
Команды перемещения данных (MOV, XCHG, LEA, LDS, LSS, LES, LFS, LGS, XLATB, PUSH, POP, IN, OUT, CBW, CWD). Назначение, ограничения.
MOV приемник, источник ; Назначение: Пересылка данных.
Базовая команда пересылки данных. Копирует содержимое источника в приемник, источник не изменяется. Команда MOV действует аналогично операторам присваивания из языков высокого уровня, то есть команда mov ax,bx эквивалентна выражению ах = bх; языка С, за исключением того, что команда ассемблера позволяет работать не только с переменными в памяти, но и со всеми регистрами процессора. В качестве источника для MOV могут использоваться: число (непосредственный операнд), регистр общего назначения, сегментный регистр или переменная (то есть операнд, находящийся в памяти). В качестве приемника — регистр общего назначения, сегментный регистр (кроме CS) или переменная. Оба операнда должны быть одного и того же размера — байт, слово или двойное слово. Нельзя выполнять пересылку данных с помощью MOV из одной переменной в другую, из одного сегментного регистра в другой и нельзя помещать в сегментный регистр непосредственный операнд — эти операции выполняют двумя командами MOV (из сегментного регистра в обычный и уже из него в другой сегментный) или парой команд PUSH/POP.
XCHG операнд1, операнд2 ; Назначение: Обмен операндов между собой.
Содержимое операнда 2 копируется в операнд 1, а старое содержимое операнда 1 — в операнд 2. XCHG можно выполнять над двумя регистрами или над регистром и переменной.
xchg eax,ebx ; то же, что три команды на языке С: ; temp = eax; eax = ebx; ebx = temp; xchg al,al ; а эта команда не делает ничегоLEA приемник, источник ; Назначение: Вычисление эффективного адреса.Команда lea похожа на команду mov тем, что она также производит пересылку. Однако, обратите внимание, команда lea производит пересылку не данных, а эффективного адреса данных (то есть смещения данных относительно начала сегмента данных) в регистр, указанный операндом назначение.lds назначение,источник ; Назначение: загрузка указателя в регистр сегмента данных ds; lss назначение,источник ; Назначение: загрузка указателя в регистр сегмента стека ss.les назначение,источник ; Назначение: загрузка указателя в регистр дополнительного сегмента данных es; lfs назначение,источник ; Назначение: загрузка указателя в регистр дополнительного сегмента данных fs;lgs назначение,источник ; Назначение: загрузка указателя в регистр дополнительного сегмента данных gs; Все команды этой группы позволяют получить в паре регистров такой полный указатель на операнд в памяти. При этом имя сегментного регистра, в который помещается сегментная составляющая адреса, определяется кодом операции. Соответственно, смещение помещается в регистр общего назначения, указанный операндом назначение. Но не все так просто с операндом источник. На самом деле, в команде в качестве источника нельзя указывать непосредственно имя операнда в памяти, на который мы бы хотели получить указатель. Предварительно необходимо получить само значение полного указателя в некоторой области памяти и указать в команде получения полного адреса имя этой области.XLATB ; Трансляция в соответствии с таблицейПомещает в AL байт из таблицы в памяти по адресу ES:BX со смещением относительно начала таблицы, равным AL. В качестве примера использования XLATB можно написать следующий вариант преобразования шестнадцатеричного числа в ASCII-код соответствующего ему символа:
mov al,0Ch
mov bx, offset htable
xlatb
если в сегменте данных, на который указывает регистр ES, было записано
htable db "0123456789ABCDEF"
то теперь AL содержит не число 0Сh, а ASCII-код буквы «С». Разумеется, это преобразование можно выполнить, используя гораздо более компактный код всего из трех арифметических команд, который будет рассмотрен в описании команды DAS, но с XLAT можно выполнять любые преобразования такого рода.
PUSH источник ; Назначение: Поместить данные в стек.
Помещает содержимое источника в стек. Источником может быть регистр, сегментный регистр, непосредственный операнд или переменная. Фактически эта команда копирует содержимое источника в память по адресу SS:[SP] и уменьшает SP на размер источника в байтах (2 или 4).
POP приемник ; Назначение: Считать данные из стека.
Помещает в приемник слово или двойное слово, находящееся в вершине стека, увеличивая SP на 2 или 4 соответственно. POP выполняет действие, полностью обратное PUSH. Приемником может быть регистр общего назначения, сегментный регистр, кроме CS (чтобы загрузить CS из стека, надо воспользоваться командой RET), или переменная.
IN приемник, источник Назначение: Считать данные из порта.
Копирует число из порта ввода-вывода, номер которого указан в источнике, в приемник. Приемником может быть только AL, АХ или ЕАХ. Источник — или непосредственный операнд, или DX, причем можно указывать только номера портов не больше 255.
OUT приемник, источник Назначение: Записать данные в порт.
Копирует число из источника (AL, АХ) в порт ввода-вывода, номер которого указан в приемнике. Приемник может быть либо непосредственным номером порта, либо регистром DX.
CBW ; Назначение: Конвертирование байта в слово.
CBW расширяет байт, находящийся в регистре AL, до слова в АХ, расширение выполняется путем установки каждого бита старшей половины результата равным старшему биту исходного байта или слова, то есть:
mov al,0F5h ; AL = 0F5h = 245 = -11
cbw ; теперь АХ = 0FFF5h = 65 525 = -11
CWD; Назначение: Конвертирование слова в двойное слово.
Команда CWD превращает слово в AХ в двойное слово, младшая половина которого (биты 0 – 15) остается в АХ, а старшая (биты 16 – 31) располагается в DX. Эта команда всего лишь устанавливает все биты регистра DX в значение, равное значению старшего бита регистра АХ, сохраняя таким образом его знак.
Все команды арифметические команды, кроме команд деления и умножения, изменяют флаги OF, SF, ZF, AF, CF, PF в соответствии с назначением каждого из этих флагов.
ADD приемник, источник – Сложение.
Команда выполняет арифметическое сложение приемника и источника, помещает сумму в приемник, не изменяя содержимое источника. Приемник может быть регистром или переменной, источник может быть числом, регистром или переменной, но нельзя использовать переменную одновременно и для источника, и для приемника. Команда ADD никак не различает числа со знаком и без знака, но, употребляя значения флагов CF (перенос при сложении чисел без знака), OF (перенос при сложении чисел со знаком) и SF (знак результата), можно использовать ее и для тех, и для других.
ADC приемник, источник – Сложение с переносом.
Эта команда во всем аналогична ADD, кроме того, что она выполняет арифметическое сложение приемника, источника и флага СF.
SUB приемник, источник – Вычитание.
Вычитает источник из приемника и помещает разность в приемник. Приемник может быть регистром или переменной, источник может быть числом, регистром или переменной, но нельзя использовать переменную одновременно и для источника, и для приемника. SUB не делает различий между числами со знаком и без знака, но флаги позволяют использовать ее как для тех, так и для других.
SBB приемник, источник – Вычитание с займом.
Эта команда во всем аналогична SUB, кроме того, что она вычитает из приемника значение источника и дополнительно вычитает значение флага CF.
CMP приемник, источник – Сравнение.
Сравнивает приемник и источник и устанавливает флаги. Сравнение осуществляется путем вычитания источника (число, регистр или переменная) из приемника (регистр или переменная; приемник и источник не могут быть переменными одновременно), причем результат вычитания никуда не записывается, единственным результатом работы этой команды оказывается изменение флагов CF, OF, SF, ZF, AF и PF.
MUL источник - Умножение чисел без знака.
Выполняет умножение содержимого источника (регистр или переменная) и регистра AL, АХ, (в зависимости от размера источника) и помещает результат в АХ, DX:AX, соответственно. Если старшая половина результата (АН, DX) содержит только нули (результат целиком поместился в младшую половину), флаги CF и OF устанавливаются в 0, иначе — в 1. Значение остальных флагов (SF, ZF, AF и PF) не определено.
IMUL источник – Умножение чисел со знаком.
IMUL приемник, источник
IMUL приемник, источник1, источник2
Эта команда имеет три формы, различающиеся числом операндов:
>IMUL источник: источник (регистр или переменная) умножается на AL, АХ (в зависимости от размера операнда), и результат располагается в АХ, DX:AX соответственно.
>IMUL приемник, источник: источник (число, регистр или переменная) умножается на приемник (регистр), и результат заносится в приемник.
>IMUL приемник,источник1,источник2: источник 1 (регистр или переменная) умножается на источник 2 (число), и результат заносится в приемник (регистр).
Во всех трех вариантах считается, что результат может занимать в два раза больше места, чем размер источника. В первом случае приемник автоматически оказывается достаточно большим, но во втором и третьем случаях могут произойти переполнение и потеря старших бит результата. Флаги OF и CF будут равны единице, если это произошло, и нулю, если результат умножения поместился целиком в приемник (во втором и третьем случаях) или в младшую половину приемника (в первом случае). Значения флагов SF, ZF, AF и PF после команды IMUL не определены.
DIV источник – Целочисленное деление без знака.
Выполняет целочисленное деление без знака AL, АХ (в зависимости от размера источника) на источник (регистр или переменная) и помещает результат в AL, АХ, а остаток — в АН, DX соответственно. Результат всегда округляется в сторону нуля, абсолютное значение остатка всегда меньше абсолютного значения делителя. Значения флагов CF, OF, SF, ZF, AF и PF после этой команды не определены, а переполнение или деление на ноль вызывает исключение #DE (ошибка при делении) в защищенном режиме и прерывание 0 — в реальном.
IDIV источник – Целочисленное деление со знаком.
Выполняет целочисленное деление со знаком AL, АХ (в зависимости от размера источника) на источник (регистр или переменная) и помещает результат в AL, АХ, а остаток — в АН, DX соответственно. Результат всегда округляется в сторону нуля, знак остатка всегда совпадает со знаком делимого, абсолютное значение остатка всегда меньше абсолютного значения делителя. Значения флагов CF, OF, SF, ZF, AF и PF после этой команды не определены, а переполнение или деление на ноль вызывает исключение #DE (ошибка при делении) в защищенном режиме и прерывание 0 — в реальном.
NEG приемник – Изменение знака.
Выполняет над числом, содержащимся в приемнике (регистр или переменная), операцию дополнения до двух. Эта операция эквивалентна обращению знака операнда, если рассматривать его как число со знаком. Если приемник равен нулю, флаг CF устанавливается в 0, иначе — в 1. Остальные флаги (OF, SF, ZF, AF, PF) устанавливаются в соответствии с результатом операции.
13.
Логические команды (AND, OR, XOR, NOT, TEST). Назначение, устанавливаемые флаги.
AND приемник, источник – Логическое И.
Команда выполняет побитовое «логическое И» над приемником (регистр или переменная) и источником (число, регистр или переменная; источник и приемник не могут быть переменными одновременно) и помещает результат в приемник. Любой бит результата равен 1, только если соответствующие биты обоих операндов были равны 1, и равен 0 в остальных случаях. Наиболее часто AND применяют для выборочного обнуления отдельных бит, например, команда
and al,00001111b
обнулит старшие четыре бита регистра AL, сохранив неизменными четыре младших. Флаги OF и CF обнуляются, SF, ZF и PF устанавливаются в соответствии с результатом, AF не определен.
OR приемник, источник – Логическое ИЛИ.
Выполняет побитовое «логическое ИЛИ» над приемником (регистр или переменная) и источником (число, регистр или переменная; источник и приемник не могут быть переменными одновременно) и помещает результат в приемник. Любой бит результата равен 0, только если соответствующие биты обоих операндов были равны 0, и равен 1 в остальных случаях. Команду OR чаще всего используют для выборочной установки отдельных бит, например, команда
or al,00001111b
приведет к тому, что младшие четыре бита регистра AL будут установлены в 1. При выполнении команды OR флаги OF и CF обнуляются, SF, ZF и PF устанавливаются в соответствии с результатом, AF не определен.
XOR приемник, источник – Логическое исключающее ИЛИ.
Выполняет побитовое «логическое исключающее ИЛИ» над приемником (регистр или переменная) и источником (число, регистр или переменная; источник и приемник не могут быть переменными одновременно) и помещает результат в приемник. Любой бит результата равен 1, если соответствующие биты операндов различны, и нулю, если одинаковы. XOR используется для самых разных операций, например:
xor ах,ах ; обнуление регистра АХ
или
xor ах,bх
xor bх,ах
xor ах,bх ; меняет местами содержимое АХ и ВХ
Оба этих примера могут выполняться быстрее, чем соответствующие очевидные команды
mov ax,0
или
xchg ax,bx
NOT приемник – Инверсия.
Каждый бит приемника (регистр или переменная), равный нулю, устанавливается в 1, и каждый бит, равный 1, сбрасывается в 0. Флаги не затрагиваются.
TEST приемник, источник – Логическое сравнение.
Вычисляет результат действия побитового «логического И» над приемником (регистр или переменная) и источником (число, регистр или переменная; источник и приемник не могут быть переменными одновременно) и устанавливает флаги SF, ZF и PF в соответствии с полученным результатом, не сохраняя результат (флаги OF и CF обнуляются, значение AF не определено). TEST, так же как и СМР, используется в основном в сочетании с командами условного перехода (Jcc), условной пересылки данных (CMOVcc) и условной установки байт (SETcc).
SAL приемник, счетчик - Арифметический сдвиг влево;
SAR приемник, счетчик - Арифметический сдвиг вправо.
Эти четыре команды выполняют двоичный сдвиг приемника (регистр или переменная) вправо (в сторону старшего бита) или влево (в сторону младшего бита) на значение счетчика (число или регистр CL, из которого учитываются только младшие пять бит, которые могут принимать значения от 0 до 31), Операция сдвига на 1 эквивалентна умножению (сдвиг влево) или делению (сдвиг вправо) на 2. Так, число 0010b (2) после сдвига на 1 влево превращается в 0100b (4). Команды SAL и SHL выполняют одну и ту же операцию (на самом деле это одна и та же команда) — на каждый шаг сдвига старший бит заносится в CF, все биты сдвигаются влево на одну позицию, и младший бит обнуляется. Команда SHR выполняет прямо противоположную операцию: младший бит заносится в CF, все биты сдвигаются на 1 вправо, старший бит обнуляется. Эта команда эквивалентна беззнаковому целочисленному делению на 2. Команда SAR действует по аналогии с SHR, только старший бит не обнуляется, а сохраняет предыдущее значение, так что, например, число 11111100b (-4) перейдет в 11111110b (-2). SAR, таким образом, эквивалентна знаковому делению на 2, но, в отличие от IDIV, округление происходит не в сторону нуля, а в сторону отрицательной бесконечности. Так, если разделить -9 на 4 с помощью IDIV, результат будет -2 (и остаток -1), а если выполнить арифметический сдвиг вправо числа -9 на 2, результат будет -3. Сдвиги больше чем на 1 эквивалентны соответствующим сдвигам на 1, выполненным последовательно.
Сдвиги на 1 изменяют значение флага OF: SAL/SHL устанавливают его в 1, если после сдвига старший бит изменился (то есть старшие два бита исходного числа не были одинаковыми), и в 0, если старший бит остался тем же. SAR устанавливает OF в 0, и SHR устанавливает OF в значение старшего бита исходного числа. Для сдвигов на несколько бит значение OF не определено. Флаги SF, ZF, PF устанавливаются всеми сдвигами в соответствии с результатом, значение AF не определено (кроме случая, если счетчик сдвига равен нулю, в котором ничего не происходит и флаги не изменяются).
В процессорах 8086 непосредственно можно было задавать в качестве второго операнда только число 1 и при использовании CL учитывать все биты, а не только младшие 5, но уже начиная с 80186 эти команды приняли свой окончательный вид.
RCL приемник, счетчик – Циклический сдвиг влево через флаг переноса;
RCR приемник, счетчик – Циклический сдвиг вправо через флаг переноса;
ROL приемник, счетчик – Циклический сдвиг влево;
ROR приемник, счетчик – Циклический сдвиг вправо.
Эти команды осуществляют циклический сдвиг приемника (регистр или переменная) на число бит, указанное в счетчике (число или регистр CL, из которого учитываются только младшие пять бит, принимающие значения от 0 до 31). При выполнении циклического сдвига на 1 команды ROR (ROL) сдвигают каждый бит приемника вправо (влево) на одну позицию, за исключением самого младшего (старшего), который записывается в позицию самого старшего (младшего) бита. Команды RCR и RCL выполняют аналогичное действие, но включают флаг CF в цикл, как если бы он был дополнительным битом в приемнике.
15.
Команды работы с двоично-десятичными числами (ААА, DAA, AAS, DAS, AAD, ААМ). Назначение, принцип их работы.
AAA –ASCII-коррекция после сложения.
Корректирует сумму двух неупакованных двоично-десятичных чисел в AL. Если коррекция приводит к десятичному переносу, АН увеличивается на 1. Эта команда имеет смысл сразу после команды сложения двух таких чисел. Например, если при сложении 05 и 06 в АХ окажется число 000Bh, то команда ААА скорректирует его в 0101h (неупакованное десятичное 11). Флаги CF и OF устанавливаются в 1, если произошел перенос из AL в АН, иначе они равны нулю. Значения флагов OF, SF, ZF и PF не определены.
DAA –BCD-коррекция после сложения. [BCD=Binary-Coded Decimal=двоично-десятичный код]
Если эта команда выполняется сразу после ADD (ADC, INC или XADD) и в регистре AL находится сумма двух упакованных двоично-десятичных чисел, то в результате в AL записывается упакованное двоично-десятичное число, которое должно было быть результатом сложения. Например, если AL содержит число 19h, последовательность команд
inc al
daa
приведет к тому, что в AL окажется 20h (а не 1Ah, как было бы после INC). Флаги AF и CF устанавливаются, если в ходе коррекции происходил перенос из первой или второй цифры соответственно, SF, ZF и PF устанавливаются в соответствии с результатом, флаг OF не определен.
AAS –ASCII-коррекция после вычитания.
Корректирует разность двух неупакованных двоично-десятичных чисел в AL сразу после команды SUB или SBB. Если коррекция приводит к займу, АН уменьшается на 1. Флаги CF и OF устанавливаются в 1, если произошел заем из AL в АН, и в ноль — в противном случае. Значения флагов OF, SF, ZF и PF не определены.
DAS –BCD-коррекция после вычитания.
Если эта команда выполняется сразу после SUB (SBB или DEC) и в регистре AL находится разность двух упакованных двоично-десятичных чисел, то в результате в AL записывается упакованное двоично-десятичное число, которое должно было быть результатом вычитания. Например, если AL содержит число 20h, последовательность команд
dec al
das
приведет к тому, что в AL окажется 19h (а не 1Fh, как было бы после DEC).
Флаги AF и CF устанавливаются, если в ходе коррекции происходил заем из первой или второй цифры соответственно, SF, ZF и PF устанавливаются в соответствии с результатом, флаг OF не определен.
AAD -ASCII-коррекция перед делением.
Выполняет коррекцию неупакованного двоично-десятичного числа, находящегося в регистре АХ, так, чтобы последующее деление привело к корректному десятичному результату. Например, разделим десятичное 25 на 5:
mov ax,0205h ; 25 в неупакованном формате
mov bl,5
aad ; теперь в АХ находится 19h
div bl ; АХ = 0005
Флаги SF, ZF и PF устанавливаются в соответствии с результатом, OF, AF и CF не определены.
AAM –ASCII-коррекция после умножения.
Корректирует результат умножения неупакованных двоично-десятичных чисел, находящийся в АХ после выполнения команды MUL, преобразовывая полученный результат в пару неупакованных двоично-десятичных чисел (в АН и AL). Например:
mov al,5
mov bl,5 ; умножить 5 на 5
mul bl ; результат в АХ - 0019h
aam ; теперь АХ содержит 0205h
ААМ устанавливает флаги SF, ZF и PF в соответствии с результатом и оставляет OF, AF и CF неопределенными.
16.
Команды переходов.Условный (Jcc) и безусловный (JMP) переход.Ограничения на дальность перехода.Короткий, близкий и дальний переход.
JMP операнд – Безусловный переход.
JMP передает управление в другую точку программы, не сохраняя какой-либо информации для возврата. Операндом может быть непосредственный адрес для перехода (в программах используют имя метки, установленной перед командой, на которую выполняется переход), а также регистр или переменная, содержащая адрес.
В зависимости от типа перехода различают:
>переход типа short (короткий переход) — если адрес перехода находится в пределах от -127 до +128 байт от команды JMP;
>переход типа near (ближний переход) — если адрес перехода находится в том же сегменте памяти, что и команда JMP;
>переход типа far (дальний переход) — если адрес перехода находится в другом сегменте. Дальний переход может выполняться и в тот же самый сегмент, если в сегментной части операнда указано число, совпадающее с текущим значением CS;
>переход с переключением задачи — передача управления другой задаче в многозадачной среде. Этот вариант будет рассмотрен в главе, посвященной защищенному режиму.
При выполнении переходов типа short и near команда JMP фактически изменяет значение регистра IP, изменяя тем самым смещение следующей исполняемой команды относительно начала сегмента кода. Если операнд — регистр или переменная в памяти, то его значение просто копируется в IP, как если бы это была команда MOV. Если операнд для JMP — непосредственно указанное число, то его значение суммируется с содержимым IP, приводя к относительному переходу. В ассемблерных программах в качестве операнда обычно указывают имена меток, но на уровне исполнимого кода ассемблер вычисляет и записывает именно относительные смещения.
Выполняя дальний переход в реальном режиме, виртуальном режиме и в защищенном режиме (при переходе в сегмент с теми же привилегиями), команда JMP просто загружает новое значение в IP и новый селектор сегмента кода в CS, используя старшие 16 бит операнда как новое значение для CS и 16 — как значение IP.
Jcc метка – Условный переход.
Это набор команд, каждая из которых выполняет переход (типа short или near), если удовлетворяется соответствующее условие. Условием в каждом случае реально является состояние тех или иных флагов, но, если команда из набора Jcc используется сразу после СМР, условия приобретают формулировки, соответствующие отношениям между операндами СМР. Например, если операнды СМР были равны, то команда JE, выполненная сразу после этого СМР, осуществит переход. Операнд для всех команд из набора Jcc — 8-битное смешение относительно текущей команды.
Команды Jcc не поддерживают дальних переходов, так что, если требуется выполнить условный переход на дальнюю метку, необходимо использовать команду из набора Jcc с обратным условием и дальний JMP, как, например:
cmp ах,0 jne local_1 jmp far_label ; переход, если АХ = 0lосаl_1:Косвенный переход.Косвенный ближний (внутрисегментный) переход. В отличие от команд прямых переходов, команды косвенных переходов могут использовать различные способы адресации и, соответственно, иметь много разных вариантов. Общим для них яштястся то, что адрес перехода не указывается явным образом в виде метки, а содержится либо в ячейке памяти, либо в одном из регистров. Это позволяет при необходимости модифицировать адрес перехода, а также осуществлять переход по известному абсолютному адресу. Рассмотрим случай, когда адрес перехода хранится в ячейке сегмента данных. Если переход ближний, то ячейка с адресом состоит из одного слова и содержит только смещение к точке перехода. code segment … jmp DS:go_addr ;Код FF 26 dddd … go: ; Точка перехода … code ends data segment … go_addr dw go ;Адрес перехода (слово) … data ends Косвенный дальний (межсегментный) переход. Как и в случае ближнего перехода, переход осуществляется по адресу, который содержится в ячейке памяти, однако эта ячейка имеет размер 2 слова, и в ней содержится полный (сегмент плюс смещение) адрес точки перехода. Программа в этом случае должна включать по меньшей мере два сегмента команд. Структура программы с использованием косвенного дальнего перехода может выглядеть следующим образом: codel segment assume CS:codel,DS:data … jmp DS:go_addr ; Код FF 2E dddd … codel ends code2 segment assume CS:code2 … go: ;Точка перехода в другом сегменте команд … code2 ends data segment … go_addr dd go ;Двухсловный адрес точки перехода … data ends Директива LOCALS для локализации меток.Метки считаются глобальными. Но обычно, на языках высокого уровня, мы ожидаем, что они будут местными. TASM позволяет нам использовать "@@" приставку к названиям меток, если мы сначала используем директиву LOCALS. Это должно находиться наверху программы, около MODEL и STACK директивы. Тогда, если мы определяем метки в пределах процедуры, они являются местными к этой процедуре, и они могут использоваться в другом месте без конфликта. 17.Команда организации циклов (LOOP). Алгоритм работы. LOOP метка – ЦиклУменьшает регистр СХ на 1 и выполняет переход типа short на метку (которая не может быть дальше, чем на расстоянии от -128 до +127 байт от команды LOOP), если СХ не равен нулю. Эта команда используется для организации циклов, в которых регистр СХ играет роль счетчика. Так, в следующем фрагменте команда ADD выполнится 10 раз:
mov cx,0Ah
loop_start: add ax,cx
loop loop_start
Команда LOOP полностью эквивалентна паре команд
dec ecx
jz метка
Но LOOP короче этих двух команд на один байт и не изменяет значения флагов.
Команды организации циклов с условием (LOOPE/LOOPZ и LOOPNE/LOOPNZ).LOOPE метка - Цикл, пока равноLOOPZ метка - Цикл, пока нольLOOPNE метка - Цикл, пока не равноLOOPNZ метка - Цикл, пока не нольВсе эти команды уменьшают регистр СХ на один, после чего выполняют переход типа short, если СХ не равен нулю и если выполняется условие. Для команд LOOPE и LOOPZ условием является равенство единице флага ZF, для команд LOOPNE и LOOPNZ — равенство флага ZF нулю. Сами команды LOOPcc не изменяют значений флагов, так что ZF должен быть установлен (или сброшен) предшествующей командой. Например, следующий фрагмент копирует строку из DS:SI в строку в ES:DI (см. описание команд работы со строками), пока не кончится строка (СХ = 0) или пока не встретится символ с ASCII-кодом 13 (конец строки): mov cx,str_lengthmove_loop: stosb lodsb cmp al,13 loopnz move_loopКоманда JCXZ.JCXZ метка - Переход, если СХ = 0Выполняет ближний переход на указанную метку, если регистр CX равен нулю. Команды не может выполнять дальних переходов. Проверка равенства СХ нулю, например, может потребоваться в начале цикла, организованного командой LOOPNE, — если в него войти с СХ = 0, то он будет выполнен 65 535 раз. 18.
Команды вызова (CALL) процедур и возврата (RET) из них. Алгоритм их работы.
CALL операнд - Вызов процедуры.
Сохраняет текущий адрес в стеке и передает управление по адресу, указанному в операнде. Операндом может быть непосредственное значение адреса (метка в ассемблерных программах), регистр или переменная, содержащие адрес перехода. Если в качестве адреса перехода указано только смещение, считается, что адрес расположен в том же сегменте, что и команда CALL. При этом, так же как и в случае с JMP, выполняется ближний вызов процедуры. Процессор помещает значение регистра IP, соответствующее следующей за CALL команде, в стек и загружает в IP новое значение, осуществляя тем самым передачу управления. Если операнд CALL — регистр или переменная, то его значение рассматривается как абсолютное смещение, если операнд — метка в программе, то ассемблер указывает ее относительное смещение. Чтобы осуществить дальний CALL в реальном режиме, режиме V86 или в защищенном режиме при переходе в сегмент с теми же привилегиями, процессор помещает в стек значения регистров CS и IP и выполняет дальний переход аналогично команде JMP.
RET число - Возврат из процедуры.
RETN считывает из стека слово (или двойное слово, в зависимости от режима адресации) и загружает его в IP, выполняя тем самым действия, обратные ближнему вызову процедуры командой, CALL. RETF соответственно загружает из стека IP и CS, возвращаясь из дальней процедуры. Если в ассемблерной программе указана команда RET, ассемблер заменит ее на RETN или RETF в зависимости от того, как была описана процедура, которую эта команда завершает. Операнд для RET необязателен, но, если он присутствует, после считывания адреса возврата из стека будет удалено указанное количество байт — это бывает нужно, если при вызове процедуры ей передавались параметры через стек.
Определение процедур в ассемблере (директивы PROC и ENDP).
Процедурой в ассемблере является все то, что в других языках называют подпрограммами, функциями, процедурами и т.д. Ассемблер не накладывает на процедуры никаких ограничений — на любой адрес программы можно передать управление командой CALL, и оно вернется к вызвавшей процедуре, как только встретится команда RET. Такая свобода выражения легко может приводить к трудночитаемым программам, и в язык ассемблера были включены директивы логического оформления процедур.
метка proc язык тип USES регистры ; TASMилиметка proc тип язык USES регистры ; MASM/WASM ... retметка endpВсе операнды PROC необязательны.Тип может принимать значения NEAR и FAR, и если он указан, все команды RET в теле процедуры будут заменены соответственно на RETN и RETF. По умолчанию подразумевается, что процедура имеет тип NEAR в моделях памяти TINY, SMALL и COMPACT.Операнд язык действует аналогично такому же операнду директивы .MODEL, определяя взаимодействие процедуры с языками высокого уровня. В некоторых ассемблерах директива PROC позволяет также считать параметры, передаваемые вызывающей программой. В этом случае указание языка необходимо, так как различные языки высокого уровня используют разные способы передачи параметров.USES — список регистров, значения которых изменяет процедура. Ассемблер помещает в начало процедуры набор команд PUSH, а перед командой RET — набор команд POP, так что значения перечисленных регистров будут восстановлены.
Директивы LOCAL, ARG и USES.
LOCAL элемент — В процедуре директива LOCAL определяет имена, которые доступны в стеке через отрицательные смещения относительно регистра BP. Если вы заканчиваете список аргументов знаком равенства (=) и идентификаторов, то этому идентификатору будет присваиваться полный объем блока локальный идентификаторов в стеке в байтах.ARG аргументЗаносит аргументы в стек для процедур. Каждому аргументу присваивается положительное смещение от регистра BP. При этом предполагается, что адрес возврата процедуры и регистр BP вызывающей программы уже занесены в стек.USES элементПоказывает, какие регистры или элементы данных, состоящие из одной лексемы, вы хотите занести в стек в начале охватывающей процедуры. Перед возвратом управления из процедуры эти регистры будут извлекаться из стека. Вы должны использовать эту директиву перед первой инструкцией, которая генерирует в процедуре реальный код. 19.Способы передачи параметров процедурам.Процедуры могут получать или не получать параметры из вызывающей процедуры и могут возвращать или не возвращать результаты.Параметры можно передавать с помощью одного из шести механизмов: -по значению; -по ссылке; -по возвращаемому значению; -по результату; -по имени; -отложенным вычислением.
Параметры можно передавать в одном из пяти мест:
-в регистрах;
-в глобальных переменных;
-в стеке;
-в потоке кода;
-в блоке параметров.
Так что всего в ассемблере возможно 30 различных способов передачи параметров для процедур.
Задание способа передачи параметров через стек. Стандартный пролог и эпилог процедур при передаче параметров через стек.Параметры помещаются в стек сразу перед вызовом процедуры. Именно этот метод используют языки высокого уровня, такие как С и Pascal. Для чтения параметров из стека обычно используют не команду POP, а регистр ВР, в который помещают адрес вершины стека после входа в процедуру: push parameter1 ; поместить параметр в стек push parameter2 call procedure add sp,4 ; освободить стек от параметров [...]procedure proc near push bp mov bp,sp(команды, которые могут использовать стек) mov ax,[bp+4] ; считать параметр 2.; Его адрес в сегменте стека ВР + 4, потому что при выполнении; команды CALL в стек поместили адрес возврата - 2 байта для процедуры; типа NEAR (или 4 - для FAR), а потом еще и ВР - 2 байта mov bx,[bp+6] ; считать параметр 1(остальные команды) рор bp retprocedure endpПараметры в стеке, адрес возврата и старое значение ВР вместе называются активационной записью функции.Упрощенный вызов процедур при передаче параметров через стек.Для удобства ссылок на параметры, переданные в стеке, внутри функции иногда используют директивы EQU, чтобы не писать каждый раз точное смещение параметра от начала активационной записи (то есть от ВР), например так: push X push Y push Z call xyzzy [...]xyzzy proc nearxyzzy_z equ [bp+8]xyzzy_y equ [bp+6]xyzzy_x equ [bp+4] push bp mov bp,sp(команды, которые могут использовать стек) mov ax,xyzzy_x ;считать параметр X(остальные команды) pop bp ret 6xyzzy endpВ обоих случаях оказывается, что оба варианта имеют свои «за» и «против», так, например, если стек освобождает процедура (командой RET число_байтов), то код программы получается меньшим, а если за освобождение стека от параметров отвечает вызывающая функция, как в нашем примере, то становится возможным вызвать несколько функций с одними и теми же параметрами просто последовательными командами CALL. Первый способ, более строгий, используется при реализации процедур в языке Pascal, а второй, дающий больше возможностей для оптимизации, — в языке С. Разумеется, если передача параметров через стек применяется и для возврата результатов работы процедуры, из стека не надо удалять все параметры, но популярные языки высокого уровня не пользуются этим методом. Кроме того, в языке С параметры помещают в стек в обратном порядке (справа налево), так что становятся возможными функции с изменяемым числом параметров (как, например, printf — первый параметр, считываемый из [ВР+4], определяет число остальных параметров). Но подробнее о тонкостях передачи параметров в стеке рассказано далее, а здесь приведен обзор методов. 20.
Понятие прерывания.
Прерывание – инициализируемый определённым образом процесс, временно переключающий микропроцессор на выполнение другой программы с последующим возобновлением выполнения прерванной программы.
Аппаратные и программные прерывания.
Сигналы аппаратных прерываний, возникающие в устройствах, входящих в состав компьютера или подключенных к нему, поступают в процессор не непосредственно, а через два контроллера прерываний, один из которых называется ведущим, а второй – ведомым.
Программные прерывания, они вызываются командой int с числовым аргументом, который рассматривается процессором, как номер вектора прерывания. Программные прерывания применяются в первую очередь для вызова системных обслуживающих программ - функций DOS и BIOS.
Таблица векторов прерываний.
Связь между номером прерывания и адресом основной памяти, соответствующим точке входа в обработчик прерывания, осуществляется через таблицу векторов прерываний, занимающую 1 Кбайт сегмента 0 основной памяти.
Команды вызова (INT) прерываний и возврата (RET) из них.
INT число - Вызов прерывания.
INT помещает в стек содержимое регистров FLAGS, CS и IP, после чего передает управление программе, называемой «обработчик прерывания» с указанным в качестве операнда номером (число от 0 до 0FFh), аналогично команде CALL. В реальном режиме адреса обработчиков прерываний считываются из таблицы, начинающейся в памяти по адресу 0000h:0000h. Адрес каждого обработчика занимает 4 байта, так что, например, адрес обработчика прерывания 10h находится в памяти по адресу 0000h:0040h. В защищенном режиме адреса обработчиков прерываний находятся в таблице IDT и обычно недоступны для прямого чтения или записи, так что для установки собственного обработчика программа должна обращаться к операционной системе. В DOS вызовы прерываний используются для выполнения большинства системных функций — работы с файлами, вводом/выводом и т.д. Например, следующий фрагмент кода завершает выполнение программы и возвращает управление DOS:
mov ax,4C01h
int 21h
IRET –Возврат из обработчика прерывания.
Возврат управления из обработчика прерывания или исключения. IRЕТ загружает из стека значения IP, CS и FLAGS.
21.
Строковые команды (MOVS, LODS, STOS, CMPS, SCAS, INS, OUTS). Назначение.
MOVS приемник, источник - Копирование строки.
MOVS ассемблер сам определяет из типа указанных операндов и копирует один байт, либо слово или двойное слово из памяти по адресу DS:SI в память по адресу ES:DI. (принято указывать имена копируемых строк, но можно использовать любые два операнда подходящего типа). Используя MOVS с операндами, можно заменить регистр DS на другой с помощью префикса замены сегмента (ES:, GS:, FS:, CS:, SS:), регистр ES заменить нельзя. После выполнения команды регистры SI и DI увеличиваются на 1, 2 или 4 (если копируются байты, слова или двойные слова), если флаг DF = 0, и уменьшаются, если DF = 1. При использовании с префиксом REP команда MOVS выполняет копирование строки длиной в СХ байт, слов или двойных слов.
LODS источник - Чтение из строки.
LODS ассемблер сам определяет из типа указанного операнда и копирует один байт, либо слово или двойное слово из памяти по адресу DS:SI в регистр AL, АХ. (принято указывать имя строки, но можно использовать любой операнд подходящего типа). Используя LODS с операндом, можно заменить регистр DS на другой с помощью префикса замены сегмента (ES:, GS:, FS:, CS:, SS:). После выполнения команды регистр SI увеличивается на 1, 2 или 4 (если считывается байт, слово или двойное слово), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда LODS выполнит копирование строки длиной в СХ, что приведет к тому, что в аккумуляторе окажется последний элемент строки. На самом деле эту команду используют без префиксов, часто внутри цикла в паре с командой STOS, так что LODS считывает число, другие команды выполняют над ним какие-нибудь действия, а затем STOS записывает измененное число в то же место в памяти.
STOS приемник – Запись в строку.
Ассемблер сам определяет тип и копирует регистр AL, AX в память по адресу ES:DI. (принято указывать имя строки, но можно использовать любой операнд подходящего типа), После выполнения команды регистр DI увеличивается на 1, 2 или 4 (если копируется байт, слово или двойное слово), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда STOS заполнит строку длиной в ЕСХ (или СХ) числом, находящимся в аккумуляторе.
CMPS приемник, источник - Сравнение строк.
Ассемблер сам определяет из типа и сравнивает один байт, слово или двойное слово из памяти по адресу DS:SI с байтом, словом или двойным словом по адресу ES:DI и устанавливает флаги аналогично команде СМР. (принято указывать имена сравниваемых строк, но можно использовать любые два операнда подходящего типа). Используя CMPS с операндами, можно заменить регистр DS на другой, применяя префикс замены сегмента (ES:, GS:, FS:, CS:, SS:), регистр ES заменить нельзя. После выполнения команды регистры SI и DI увеличиваются на 1, 2 или 4 (если сравниваются байты, слова или двойные слова), если флаг DF = 0, и уменьшаются, если DF = 1. При использовании с префиксом REP команда CMPS выполняет сравнение строки длиной в СХ байт, слов или двойных слов, но чаще ее используют с префиксами REPNE/REPNZ или REPE/REPZ. В первом случае сравнение продолжается до первого несовпадения в сравниваемых строках, а во втором — до первого совпадения.
SCAS приемник - Сканирование строки.
Ассемблер сам определяет из типа и сравнивает содержимое регистра AL, AX с байтом, словом или двойным словом из памяти по адресу ES:DI и устанавливает флаги аналогично команде СМР. (принято указывать имя сканируемой строки, но можно использовать любой операнд подходящего типа). После выполнения команды регистр DI увеличивается на 1, 2 или 4 (если сканируются байты, слова или двойные слова), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда SCAS выполняет сканирование строки длиной в СХ байт, слов или двойных слов, но чаще ее используют с префиксами REPNE/REPNZ или REPE/REPZ. В первом случае сканирование продолжается до первого элемента строки, отличного от содержимого аккумулятора, а во втором — до первого совпадающего.
INS источник, DX - Чтение строки из порта.
Ассемблер определяет тип и считывает из порта ввода-вывода, номер которого указан в регистре DX, байт, слово или двойное слово в память по адресу ES:DI. После выполнения команды регистр DI увеличивается на 1, 2 или 4 (если считывается байт, слово или двойное слово), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда INS считывает блок данных из порта длиной в СХ байт, слов или двойных слов.
OUTS DX, приемник – Запись строки в порт.
Ассемблер определяет тип и записывает в порт ввода-вывода, номер которого указан в регистре DX, байт, слово или двойное слово из памяти по адресу DS:SI. Используя OUTS с операндами, также можно заменить регистр DS на другой с помощью префикса замены сегмента (ES:, GS:, FS:, CS:, SS:). После выполнения команды регистр ESI (SI) увеличивается на 1, 2 или 4 (если считывается байт, слово или двойное слово), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда OUTS записывает блок данных размером в СХ байт, слов или двойных слов в указанный порт.
Префиксы повторения строковых команд (REP, REPE/REPZ, REPNE/REPNZ).
REP –Повторять;
REPE -Повторять, пока равно;
REPZ -Повторять, пока ноль;
REPNE -Повторять, пока не равно;
REPNZ -Повторять, пока не ноль.
Все эти команды — префиксы для операций над строками. Любой из префиксов выполняет следующую за ним команду строковой обработки столько раз, сколько указано в регистре СХ, уменьшая его при каждом выполнении команды на 1. Кроме того, префиксы REPZ и REPE прекращают повторения команды, если флаг ZF сброшен в 0, и префиксы REPNZ и REPNE прекращают повторения, если флаг ZF установлен в 1. Префикс REP обычно используется с командами INS, OUTS, MOVS, LODS, STOS, а префиксы REPE, REPNE, REPZ и REPNZ — с командами CMPS и SCAS. Поведение префиксов не с командами строковой обработки не определено.
22.Команды работы с флагами.
STC –[SeT Carry]Установить флаг переноса. Устанавливает флаг CF в 1.
CLC – [CLear Carry] Сбросить флаг переноса. Сбрасывает флаг CF в 0.
CMC – [CoMplement Carry] Инвертировать флаг переноса. Инвертирует флаг СF.
STD –[SeT Direction]Установить флаг направления. Устанавливает флаг DF в 1, так что при последующих строковых операциях регистры DI и SI будут уменьшаться.
CLD -[CLear Direction] Сбросить флаг направления. Сбрасывает флаг DF в 0, так что при последующих строковых операциях регистры DI и SI будут увеличиваться.
LAHF – [Load Flags into AH]Загрузить флаги состояния в АН. Копирует младший байт регистра FLAGS в АН, включая флаги SF (7 бит), ZF (6 бит), AF (4 бит), PF (2 бит) и CF (0 бит). Бит 1 устанавливается в 1, биты 3 и 5 - в 0.
SAHF– [Store AH into Flags] Загрузить флаги состояния из АН. Загружает флаги SF, ZF, AF, PF и CF из регистра АН значениями бит 7, 6, 4, 2 и 0 соответственно. Зарезервированные биты 1, 3 и 5 регистра флагов не изменяются.
CLI – [CLear Interrupt]Запретить прерывания. Сбрасывает флаг IF в 0. После выполнения этой команды процессор игнорирует все прерывания от внешних устройств.
STI – [SeT Interrupt]Разрешить прерывания. Устанавливает флаг IF в 1, отменяя тем самым действие команды CLI.
SALC – [Store AL … Carry]Установить AL в соответствии с CF.
Устанавливает AL в 0FFh, если флаг CF = 1, и сбрасывает в 00h, если CF = 0. Действие SALC аналогично SBB AL,AL, но SALC не изменяет значений флагов.
PUSHF – [PUSH Flags]Поместить FLAGS в стек. Эти команды копируют содержание регистра FLAGS в стек (уменьшая SP на 2).
POPF – [POP Flags]Загрузить FLAGS из стека. Считывает из вершины стека слово (POPF) и помещает в регистр FLAGS (увеличивает SP на 2).
Команды NOP и HALT.
NOP –[No-OPeration instruction]Отсутствие операции.
Однобайтная команда (код 90h), которая не выполняет ничего, только занимает место и время. Код этой команды фактически соответствует команде XCHG AL,AL. Можно многие команды записать так, что они не будут приводить ни к каким действиям, например:
mov ax,ax ; 2 байта
xchg ax,ax ; 2 байта
lea bx,[bx+0] ; 3 байта (8Dh, 5Fh, 00h, но многие
; ассемблеры, встретив такую команду,
; реально используют более короткую команду
; lea bx,[bx] с кодом 8Dh 1Fh)
shl eax,0 ; 4 байта
shrd еах,еах,0 ; 5 байт
HLT – [HaLT] Команда приостановки процессора до тех пор, пока не возникнет аппаратное прерывание. Когда процессор находится в стадии исполнения этой команды он потребляет минимальное количество энергии, и, следовательно, почти что не выделяет тепла.
23.
Понятие макроопределения.
Макроопределением (или макросом) называется участок программы, которому присвоено имя и который ассемблируется всякий раз, когда ассемблер встречает это имя в тексте программы.
Директивы определения макросов (MACRO и ENDM).
Макрос начинается директивой MACRO и заканчивается ENDM. Например: пусть описано макроопределение hex2ascii, переводящее шестнадцатеричное число, находящееся в регистре AL, в ASCII-код соответствующей шестнадцатеричной цифры:
hex2ascii macro cmp al,10 sbb al,69h das endm
Теперь в программе можно использовать слово hex2ascii, как если бы это было имя команды, и ассемблер заменит каждое такое слово на три команды, содержащиеся в макроопределении. Разумеется, можно оформить этот же участок кода в виде процедуры и вызывать его командой CALL — если процедура вызывается больше одного раза, этот вариант программы займет меньше места, но вариант с макроопределением станет выполняться быстрее, так как в нем не будет лишних команд CALL и RET. Однако скорость выполнения — не главное преимущество макросов. В отличие от процедур макроопределения могут вызываться с параметрами, следовательно, в зависимости от ситуации, включаемый код будет немного различаться, например:
s_mov macro register1,register2 push register1 pop register2 endm
Теперь можно использовать S_MOV вместо команды MOV для того, чтобы скопировать значение из одного сегментного регистра в другой.
Условные директивы и их использование в макросах.
if выражение...endif
Если значение выражения — ноль (ложь), весь участок программы между IF и ENDIF игнорируется. Директива IF может также сочетаться с ELSE и ELSEIF:
if выражение...else...endif
Если значение выражения — ноль, ассемблируется участок программы от ELSE до ENDIF, в противном случае — от IF до ELSE.
if выражение1...elseif выражение2...elseif выражение3...else...endif
Так, если, например, выражение 2 не равно нулю, будет ассемблироваться участок программы между первой и второй директивой ELSEIF. Если все три выражения равны нулю, ассемблируется фрагмент от ELSE до ENDIF. Данная структура директив может использоваться в частном случае аналогично операторам switch/case языков высокого уровня, если выражения — проверки некоторой константы на равенство.
IF1/ELSEIF1 — если ассемблер выполняет первый проход ассемблирования;
IF2/ELSEIF2 — если ассемблер выполняет второй проход ассемблирования (часто не работает на современных ассемблерах);
IFE выражение/ELSEIFE выражение — если выражение равно нулю (ложно);
IFDEF метка/ELSEIFDEF метка — если метка определена;
IFNDEF метка/ELSEIFNDEF метка — если метка не определена;
IFB <аргумент>/ELSEIFB <аргумент> — если значение аргумента — пробел (эти и все следующие директивы используются в макроопределениях для проверки параметров);
IFNB <аргумент>/ELSEIFNB <аргумент> — если значение аргумента — не пробел (используется в макроопределениях для проверки переданных параметров);
IFDIF <арг1>,<арг2>/ELSEIFDIF <арг1>,<арг2> — если аргументы отличаются (с различием больших и маленьких букв);
IFDIFI <арг1>,<арг2>/ELSEIFDIFI <арг1>,<арг2> — если аргументы отличаются (без различия больших и маленьких букв);
IFIDN <арг1>,<арг2>/ELSEIFIDN <арг1>,<арг2> — если аргументы одинаковы (с различием больших и маленьких букв);
IFIDNI <арг1>,<арг2>/ELSEIFIDNI <арг1>,<арг2> — если аргументы одинаковы (без различия больших и маленьких букв).
Иногда директивы условного ассемблирования используются для того, чтобы прервать ассемблирование программы, если обнаружилась какая-нибудь ошибка. Для таких случаев предназначены директивы условной генерации ошибок.
if $ gt 65535 ; Если адрес вышел за пределы сегмента..errendif
Встретив директиву .ERR, ассемблер прекратит работу с сообщением об ошибке. Аналогично командам условного ассемблирования существуют модификации команды .ERR:
.ERR1 — ошибка при первом проходе ассемблирования;
.ERR2 — ошибка при втором проходе ассемблирования;
.ERRE выражение — ошибка, если выражение равно нулю (ложно);
.ERRNZ выражение — ошибка, если выражение не равно нулю (истинно);
.ERRDEF метка — ошибка, если метка определена;
.ERRNDEF метка — ошибка, если метка не определена;
.ERRB <аргумент> — ошибка, если аргумент пуст (эта и все следующие директивы используются в макроопределениях для проверки параметров);
.ERRNB <аргумент> — ошибка, если аргумент не пуст;
.ERRDIF <арг1>,<арг2> — ошибка, если аргументы различны;
.ERRDIFI <арг1>,<арг2> — ошибка, если аргументы отличаются (сравнение не различает большие и маленькие буквы);
.ERRIDN <арг1>,<арг2> — ошибка, если аргументы совпадают;
.ERRIDNI <арг1>,<арг2> — ошибка, если аргументы совпадают (сравнение не различает большие и маленькие буквы).
Например: напишем макрос, выполняющий умножение регистра AX на число, причем, если множитель — степень двойки, то умножение будет выполняться более быстрой командой сдвига влево.
fast_mul macro number if number eq 2 shl ax,1 ; Умножение на 2 elseif number eq 4 shl ax,2 ; Умножение на 4 elseif number eq 8 shl ax,3 ; Умножение на 8 ... ; Аналогично вплоть до: elseif number eq 32768 shl ax,15 ; Умножение на 32768 else mov dx,number ; Умножение на число, не являющееся mul dx ; степенью двойки. endif endm
Директива LOCAL и ее использование в макросах.
LOCAL метка... — перечисляет метки, которые будут применяться внутри макроопределения, чтобы не возникало ошибки «метка уже определена» при использовании макроса более одного раза или если та же метка присутствует в основном тексте программы (в WASM директива LOCAL позволяет использовать макрос с метками несколько раз, но не разрешает применять метку с тем же именем в программе). Операнд для LOCAL — метка или список меток, которые будут использоваться в макросе.
24.
Макросы повторения (REPT, IRP, IRPC).
Простейший блок повторений REPT (не поддерживается WASM) выполняет ассемблирование участка программы заданное число раз. Например, если требуется создать массив байтов, проинициализированный значениями от 0 до 0FFh, это можно сделать путем повтора псевдокоманды DB следующим образом:
hexnumber = 0hextable label byte ; Имя массива rept 256 ; Начало блока db hexnumber ; Эти две строки ассемблируютсяhexnumber = hexnumber+1 ; 256 раз. endm
Блоки повторений, так же как макроопределения, могут вызываться с параметрами. Для этого используются директивы IRP и IRPC:
Блок, описанный директивой IRP, будет вызываться столько раз, сколько значений указано в списке (в угловых скобках), и при каждом повторении будет определена метка с именем параметр, равная очередному значению из списка. Например, следующий блок повторений сохранит в стек регистры AX, BX, CX и DX:
irp reg,<ax,bx,cx,dx> push reg endm
Директива IRPC (FORC в WASM) описывает блок, который выполняется столько раз, сколько символов содержит указанная строка, и при каждом повторении будет определена метка с именем параметр, равная очередному символу из строки. Если строка содержит пробелы или другие символы, отличные от разрешенных для меток, она должна быть заключена в угловые скобки. Например, следующий блок задает строку в памяти, располагая после каждого символа строки атрибут 0Fh (белый символ на черном фоне), так что эту строку впоследствии можно будет скопировать прямо в видеопамять.
irpc character,<строка символов> db ’&character&’,0Fh endm
В этом примере используются амперсанды, чтобы вместо параметра character было подставлено его значение даже внутри кавычек. Амперсанд — это один из макрооператоров — специальных операторов, которые действуют только внутри макроопределений и блоков повторений.
Директивы работы с текстовыми макросами.
Макрооператор & (амперсанд) нужен для того, чтобы параметр, переданный в качестве операнда макроопределению или блоку повторений, заменялся значением до обработки строки ассемблером. Так, например, следующий макрос выполнит команду PUSH EAX, если его вызвать как PUSHREG A:
pushreg macro letter push e&letter&x endm
Иногда можно использовать только один амперсанд — в начале параметра, если не возникает неоднозначностей. Например, если передается номер, а требуется создать набор переменных с именами, оканчивающимися этим номером:
irp number,<1,2,3,4>msg&number db ? endm
Макрооператор <> (угловые скобки) действует так, что весь текст, заключенный в эти скобки, рассматривается как текстовая строка, даже если он содержит пробелы или другие разделители. Как мы уже видели, этот макрооператор используется при передаче текстовых строк в качестве параметров для макросов. Другое частое применение угловых скобок — передача списка параметров вложенному макроопределению или блоку повторений.
Макрооператор ! (восклицательный знак) используется аналогично угловым скобкам, но действует только на один следующий символ, так что, если этот символ — запятая или угловая скобка, он все равно будет передан макросу как часть параметра.
Макрооператор % (процент) указывает, что находящийся за ним текст является выражением и должен быть вычислен. Обычно это требуется для того, чтобы передавать в качестве параметра в макрос не само выражение, а его результат.
Макрооператор ;; (две точки с запятой) — начало макрокомментария. В отличие от обычных комментариев текст макрокомментария не попадает в листинг и в текст программы при подстановке макроса. Это сэкономит память при ассемблировании программы с большим количеством макроопределений.
25.
Многомодульная разработка программ.
Во многих языках программирования можно описывать процедуры внутри друг друга, так что локальные переменные, объявленные в пределах одной процедуры, доступны только из этой процедуры и всех вложенных в нее. Разные языки программирования используют разные способы реализации доступа к переменным, объявленным в функциях с меньшим уровнем вложенности (уровень вложенности главной процедуры определяют как 0 и увеличивают на 1 с каждым новым вложением).
Директивы описания внешних процедур и данных.
public язык метка... ; Для TASM и MASM
или
public метка язык... ; для WASM
Метка, объявленная директивой PUBLIC, становится доступной для других модулей программы. Так, можно объявлять имена процедур, переменные и константы, определенные директивой EQU. Необязательный операнд языка (C, PASCAL, BASIC, FORTRAN, SYSCALL или STDCALL) указывает, ч