Assembler - язык неограниченных возможностей

         

Программирование на ассемблере в среде UNIX

Адресация


Регистровый операнд всегда начинается с символа «%»:

// xor edx,edx xorl %eax,%eax

Непосредственный операнд всегда начинается с символа «$»:

// mov edx,offset variable movl $variable,%edx

Косвенная адресация использует немодифицированное имя переменной:

// push dword ptr variable pushl variable

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

// mov eax,base_addr[ebx+edi*4] (наиболее общий случай) movl base_addr(%ebx,%edi,4),%еах // lea eax,[eax+eax*4] leal (%еах,%еах,4),%еах // mov ax,word ptr [bp-2] movw -2(%ebp),%ax // mov edx,dword ptr [edi*2] movl (,%edi,2),%edx



Блоки повторения


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

.rept число повторов .endr

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

.irp симол, значение... .endr

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

.irpc символ, строка .endr

Внутри блока повторения на символ можно ссылаться, начиная его с обратной косой черты, то есть как \символ, например такой блок:

.irp param,1,2,3 movl %st(0),%st(\param) . endr

как и такой:

.irpc param,123 movl %st(0),%st(\param) .endr

ассемблируется в:

movl %st(0),%st(1) movl %st(0),%st(2) movl %st(0),%st(3)



Директивы ассемблера


Все директивы ассемблера в UNIX всегда начинаются с символа «.» (точка). Из-за большого числа отличающихся операционных систем и ассемблеров для них возникло много часто встречающихся директив. Рассмотрим только наиболее полезные.





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


Эти директивы эквивалентны директивам db, dw, dd, df и т.п., применяющимся в ассемблерах для DOS/Windows. Основное отличие здесь состоит в том, чтобы дать имя переменной, значение которой определяется такой директивой; в ассемблерах для UNIX обязательно надо ставить полноценную метку, заканчивающуюся двоеточием.

Байты:

.byte выражение...

Слова:

.word выражение... или .hword выражение... или .short выражение...

Двойные слова:

.int выражение... или .long выражение...

Учетверенные слова (8-байтные переменные):

.quad выражение...

16-байтные переменные (окта-слова):

. octa выражение...

32-битные числа с плавающей запятой:

.float число... или .single число...

64-битные числа с плавающей запятой:

.double число...

80-битные числа с плавающей запятой:

.tfloat число...

Строки байтов:

.ascii строка...

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

.asciz строка... или .string строка

Блоки повторяющихся данных:

.skip размер,значение или .space размер,значение

Заполняет области памяти указанного размера байтами с заданным значением

.fill повтор, размер, значение

Заполняет область памяти значениями заданного размера (0 – 8 байт) указанное число раз. По умолчанию размер принимается равным 1, а значение — 0.

Неинициализированные переменные:

.lcomm символ, длина, выравнивание

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



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


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

.data подсекция

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

.text подсекция

Следующие команды будут ассемблироваться в секцию кода.

.section имя, флаги, @тип или .section "имя", флаги

Общее определение новой секции:

флаги (для ELF):

w или #write — разрешена запись;

х или #execinstr — разрешено исполнение;

а или #alloc — разрешено динамическое выделение памяти (.bss);

тип (для ELF):

©progbits — содержит данные;

@nobits — не содержит данные (только занимает место).



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


Включить текст другого файла в программу:

.include файл

Ассемблировать блок, если выполняется условие или определен или не определен символ:

.if выражение .ifdef символ .ifndef символ или .ifnotdef символ .else .endif

Выдать сообщение об ошибке:

.err

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

.abort



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


Запретить листинг:

.nolist

Разрешить листинг:

.list

Конец страницы:

.eject

Размер страницы (60 строк, 200 столбцов по умолчанию):

.psize строки, столбцы

Заголовок листинга:

.title текст

Подзаголовок:

.sbttl текст



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


.align выражение, выражение, выражение

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

В некоторых системах первое выражение — не число, кратным которому должен стать указатель, а число бит в указателе, которые должны стать нулевыми (в нашем примере это было бы 4).

.org новое значение, заполнение

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



Директивы управления разрядностью


.code16

Следующие команды будут ассемблироваться как 16-битные.

.code32

Отменяет действие .code 16.



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


Присвоение значений символам:

.equ символ, выражение

Присваивает символу значение выражения.

.equiv символ, выражение

То же, что и .equ, но выдает сообщение об ошибке, если символ определен.

.set символ, выражение

То же, что и .equ, но можно делать несколько раз. Обычно, впрочем, бывает удобнее написать просто «символ = выражение».

Управление внешними символами:

.globl символ или .global символ

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

.extern символ

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

.comm символ, длина, выравнивание

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

Описание отладочных символов:

.def символ .endef

Блок описания отладочного символа.

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



Инфиксные, или бинарные операторы


Высшего приоритета:

* — умножение

/ — целочисленное деление

% — остаток

< или << — сдвиг влево

> или >> — сдвиг вправо

Среднего приоритета:

| — побитовое «ИЛИ»

& — побитовое «И»

^ — побитовое «исключающее ИЛИ»

! — побитовое «ИЛИ-НЕ» (логическая импликация)

Низшего приоритета:

+ — сложение

– — вычитание



Макроопределения


Начало макроопределения:

.macro имя, аргументы

Конец макроопределения:

.endm

Преждевременный выход из макроопределения:

.exitm

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

Хотя стандартные директивы и включают в себя такие вещи, как блоки повторений и макроопределения, их реализация достаточно упрощена, и при программировании для UNIX на ассемблере часто используют дополнительные препроцессоры. Долгое время было принято использовать С-препроцессор или М4, и многие ассемблеры даже могут вызывать их автоматически, но в рамках проекта GNU был создан специальный препроцессор для ассемблера — gasp. Gasp включает различные расширения вариантов условного ассемблирования, построения циклов, макроопределений, листингов, директив определения данных и так далее. Мы не будем заниматься реализацией таких сложных программ, которым может потребоваться gasp, мы даже не воспользуемся и половиной перечисленных директив, но существование этого препроцессора следует иметь в виду.



Операторы ассемблера


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

// поместить в ЕАХ число 320 * 200 movl $320*$200, %еах

В этих выражениях встречаются следующие операторы.



Основные правила


Итак, в ассемблере AT&T в качестве допустимых символов в тексте программы рассматриваются только латинские буквы, цифры и символы «%» (процент) «$» (доллар), «*» (звездочка) , «.» (точка), «,» (запятая) и «_» (подчеркивание). Помимо них существуют символы начала комментария, различные для разных ассемблеров и различные для комментария размером в целую строку или правую часть строки. Любые другие символы, кроме кавычек, двоеточия, пробела и табуляции, если они не часть комментария или не заключены в кавычки, считаются ошибочными.

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

// остановить процессор hlt

Если последовательность допустимых символов начинается с символа «%» — это название регистра процессора:

// поместить в стек содержимое регистра ЕАХ pushl %eax

Если последовательность начинается с символа «$» — это непосредственный операнд:

// поместить в стек 0, число 10h и адрес переменной variable pushl $0 pushl $0x10 pushl $variable

Если последовательность символов начинается с точки — это директива ассемблера:

.align 2

Если последовательность символов, с которой начинается строка, заканчивается двоеточием — это метка (внутренняя переменная ассемблера, значение которой соответствует адресу в указанной точке):

eternal_loop: jmp eternal_loop variable: .byte 7

Метки, состоящие из одной цифры от 0: до 9:, используются как локальные — обращение к метке 1f соответствует обращению к ближайшей из меток 1: вперед по тексту программы, обращение к метке 4b соответствует обращению к ближайшей из меток 4: назад по тексту программы.

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

Специальная метка «.» (точка) всегда равна текущему адресу (в точности как «$» в ассемблерах для DOS/Windows).

Если последовательность символов начинается с символа «*» — это абсолютный адрес (для команд jmp и call), иначе — относительный.



Префиксные, или унарные операторы


– (минус) — отрицательное число

~ (тильда) — «логическое НЕ»



Программирование без использования libc


Может оказаться, что программа вынуждена многократно вызывать те или иные стандартные функции из libc в критическом участке, тормозящем выполнение всей программы. В этом случае стоит обратить внимание на то, что многие функции libc на самом деле всего лишь более удобный для языка С интерфейс к системным вызовам, предоставляемым самим ядром операционной системы. Такие операции, как ввод/вывод, вся работа с файловой системой, с процессами, с TCP/IP и т.п., могут выполняться путем передачи управления ядру операционной системы напрямую.

Чтобы осуществить системный вызов, надо передать его номер и параметры на точку входа ядра аналогично функции libc syscall(2). Номера системных вызовов (находятся в файле /usr/include/sys/syscall.h) и способ обращения к точке входа (дальний call по адресу 0007:00000000) стандартизированы SysV/386 ABI, но, например в Linux, используется другой механизм — прерывание 80h, так что получается, что обращение к ядру операционной системы напрямую делает программу привязанной к этой конкретной системе. Часть этих ограничений можно убрать, используя соответствующие #define, но в общем случае этот выигрыш в скорости оборачивается еще большей потерей переносимости, чем само использование ассемблера в UNIX.

Посмотрим, как реализуются системные вызовы в рассматриваемых нами примерах:

// hellolnx.s // Программа, выводящая сообщение "Hello world" на Linux // без использования libc // // Компиляция: // as -о hellolnx.o hellolnx.s // ld -s -o hellolnx hellolnx.o // .text .globl _start _start: // системный вызов #4 "write", параметры в Linux помещают слева направо, // в регистры %еах, %ebx, %ecx, %edx, %esi, %edi movl $4,%eax xorl %ebx,%ebx incl %ebx // %ebx = 1 (идентификатор stdout) movl $message,%ecx movl $message_l,%edx // передача управления в ядро системы - прерывание с номером 80h int $0x80

// системный вызов #1 "exit" (%еах = 1, %ebx = 0) xorl %eax,%eax incl %eax xorl %ebx,%ebx int $0x80 hlt

.data message: .string "Hello world\012" message_l = . - message


Linux — это довольно уникальный случай в отношении системных вызовов. В более традиционных UNIX-системах — FreeBSD и Solaris — системные вызовы реализованы согласно общему стандарту SysV/386, и различие в программах заключается только в том, что ассемблер, поставляемый с FreeBSD, не поддерживает некоторые команды и директивы.

// hellobsd.s // Программа, выводящая сообщение "Hello world" на FreeBSD // без использования libc // // Компиляция: // as -о hellobsd.o hellobsd.s // ld -s -o hellobsd hellobsd.o // .text .globl _start _start: // системная функция 4 "write" // в FreeBSD номер вызова помещают в %еах, а параметры - в стек // справа налево плюс одно двойное слово pushl $message_l // параметр 4 - длина буфера pushl $message // параметр 3 - адрес буфера pushl $1 // параметр 2 - идентификатор устройства movl $4,%еах // параметр 1 - номер функции в еах pushl %eax // в стек надо поместить любое двойное слово, но мы поместим номер вызова // для совместимости с Solaris и другими строгими операционными системами // lcall $7,$0 - ассемблер для FreeBSD не поддерживает эту команду .byte 0x9a .long 0 .word 7 // восстановить стек addl $12,%esp // системный вызов 1 "exit" xorl %eax,%eax pushl %eax incl %eax pushl %eax // lcall $7,$0 .byte 0x9A .long 0 .word 7 hlt

.data message: .ascii "Hello world\012" message_l = . - message

И теперь то же самое в Solaris:

// hellosol.s // Программа, выводящая сообщение "Hello world" на Solaris/x86 // без использования libc // // Компиляция: // as -о hellosol.o hellosol.s // ld -s -o hellosol hellosol.o .text .globl _start _start: // комментарии - см. hellobsd.s pushl $message_l pushl $message movl $4,%eax pushl %eax lcall $7,$0 addl $16,%esp

xorl %eax,%eax pushl %eax incl %eax pushl %eax lcall $7,$0 hit

.data message: .string "Hello world\012" message_l = . - message

Конечно, создавая эти программы, мы нарушили спецификацию SysV/386 ABI несколько раз, но из-за того, что мы не обращались ни к каким разделяемым библиотекам, это прошло незамеченным. Требования к полноценной программе сильно разнятся в различных операционных системах, и все они выполнены с максимально возможной тщательностью в файлах crt*.o, которые мы подключали в примере с использованием библиотечных функций. Поэтому, если вы не ставите себе цель сделать программу абсолютно минимального размера, гораздо удобнее назвать свою процедуру main (или _main) и добавлять crt*.o и -lс при компоновке.


Программирование на ассемблере в среде UNIX


Операционная система MS-DOS, получившая дальнейшее развитие в виде Windows, долгое время была практически единственной операционной системой для персональных компьютеров на базе процессоров Intel. Но с течением времени мощность процессоров выросла настолько, что для них стало возможным работать под управлением операционных систем класса UNIX, использовавшихся обычно на более мощных компьютерах других компаний. В настоящее время существует более двадцати операционных систем для Intel, представляющих те или иные диалекты UNIX. Мы рассмотрим наиболее популярные из них.

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

FreeBSD — бесплатно распространяемая операционная система, представляющая вариант BSD UNIX. Считается наиболее стабильной из UNIX-систем для Intel.

Solaris/x86 — коммерческая операционная система компании Sun Microsystems, представляющая вариант System V UNIX, изначально созданная для компьютеров Sun, существует в версии для Intel 80x86. Распространяется бесплатно для образовательных целей.

Несмотря на то что при программировании для UNIX обычно употребляется исключительно язык С, пользоваться ассемблером в этих системах можно, и даже крайне просто. Программы в UNIX выполняются в защищенном режиме с моделью памяти flat и могут вызывать любые функции из библиотеки libc или других библиотек точно так же, как это делают программы на С. Конечно, круг задач, для которых имеет смысл использовать ассемблер в UNIX, ограничен. Если вы не занимаетесь разработкой ядра операционной системы или, например, эмулятора DOS, практически все можно сделать и на С, но иногда встречаются ситуации, когда требуется что-то особенное. Написать процедуру, выполняющую что-то как можно быстрее (например, воспроизведение звука из файла в формате МР3), или программу, использующую память более эффективно (хотя это часто можно повторить на С), или программу, использующую возможности нового процессора, поддержка которого еще не добавлена в компилятор (если вы знаете ассемблер для UNIX), достаточно просто.



Программирование с использованием libc


Все программы для UNIX, написанные на С, постоянно обращаются к различным функциям, находящимся в libc.so или других стандартных или нестандартных библиотеках. Программы и процедуры на ассемблере, естественно, могут делать то же самое. Вызов библиотечной функции выполняется обычной командой call, а передача параметров осуществляется в соответствии с С-конвенцией: параметры помещают в стек справа налево и очищают стек после вызова функции. Единственная сложность здесь состоит в том, что к имени вызываемой функции в некоторых системах, например FreeBSD, приписывается в начале символ подчеркивания, в то время как в других (Linux и Solaris) имя не изменяется. Если имена в системе модифицируются, имена процедур, включая main(), написанных на ассемблере, также должны быть изменены заранее.

Посмотрим на примере программы, выводящей традиционное сообщение «Hello world», как это делается:

// helloelf.s // Минимальная программа, выводящая сообщение "Hello world" // Для компиляции в формат ELF // // Компиляция: // as -о helloelf.o helloelf.s // Компоновка: // (пути к файлу crt1.o могут отличаться на других системах) // Solaris с SunPro С // ld -s -о helloelf.sol helloelf.o /opt/SUNWspro/SC4.2/lib/crt1.о -lс // Solaris с GNU С // ld -s -o helloelf.gso helloelf.o // /opt/gnu/lib/gcc-lib/i586-cubbi-solaris2.5.1/2.7.2.3.f.1/crt1.о -lс // Linux // ld -s -m elf_i386 -o helloelf.lnx /usr/lib/crt1.o /usr/lib/crti.o // -L/usr/lib/gcc-lib/i586-cubbi-linuxlibc1/2.7.2 helloelf.o -lc -lgcc // /usr/lib/crtn.o // .text // код, находящийся в файлах crt*.o, передаст управление на процедуру main // после настройки всех параметров .globl main main: // поместить параметр (адрес строки message) в стек pushl $message // вызвать функцию puts (message) call puts // очистить стек от параметров popl %ebx // завершить программу ret

.data message: .string "Hello world\0"

В случае FreeBSD придется внести всего два изменения — добавить символ подчеркивания в начало имен функций puts и main и заменить директиву .string на .ascii, так как версия ассемблера, обычно распространяемого с FreeBSD, .string не понимает.

// hellocof.s // Минимальная программа, выводящая сообщение "Hello world" // Для компиляции в вариант формата COFF, используемый во FreeBSD // Компиляция для FreeBSD: // as -о hellocof.o hellocof.s // ld -s -о hellocof.bsd /usr/lib/crt0.o hellocof.o -lc


.text .globl _main _main: pushl $message call _puts popl %ebx ret

.data message: .ascii "Hello world\0"

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

Лучшее применение ассемблера для UNIX (кроме собственно разработки ядра системы) все-таки остается за небольшими процедурами, требующими большой вычислительной мощности, — кодированием, архивированием, преобразованиями типа Фурье, которые не очень сложны и при необходимости могут быть легко переписаны заново на ассемблере для другого процессора или на С.


Синтаксис AT&T


Проблема в том, что ассемблер для UNIX кардинально отличается от того, что рассматривалось в этой книге до сих пор. В то время как основные ассемблеры для MS-DOS и Windows используют синтаксис, предложенный компанией Intel, изобилующий неоднозначностями, часть которых решается за счет использования поясняющих операторов типа byte ptr, word ptr или dword ptr, а часть не решается вообще (все те случаи, когда приходится указывать код команды вручную), в UNIX с самого начала используется вариант универсального синтаксиса AT&T, синтаксис SysV/386, который специально создавался с целью устранения неоднозначностей в толковании команд. Вообще говоря, существует и ассемблер для DOS/Windows, использующий АТ&Т-синтаксис, — это gas, входящий в набор средств разработки DJGPP, а также есть ассемблер, использующий Intel-синтаксис и способный создавать объектные файлы в формате ELF, применяемом в большинстве UNIX-систем, — это бесплатно распространяемый в сети Inetrnet ассемблер NASM. Мы будем рассматривать только ассемблеры, непосредственно входящие в состав операционных систем, то есть ассемблеры, которые вызываются стандартной командой as.



Запись команд


Названия команд, не принимающих операндов, совпадают с названиями, принятыми в синтаксисе Intel:

nop

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

b — байт;

w — слово;

l — двойное слово;

q — учетверенное слово;

s — 32-битное число с плавающей запятой;

l — 64-битное число с плавающей запятой;

t — 80-битное число с плавающей запятой.

// mov byte ptr variable,0 movb $0,variable // fild qword ptr variable fildq variable

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

// movsx edx,al movsbl %al,%edx

Команды преобразования типов имеют в AT&T названия из четырех букв — С, размер источника, Т и размер приемника:

// cbw cbtw // cwde cwtl // cwd cwtl // cdq cltd

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

Дальние команды передачи управления (jmp, call, ret) отличаются от ближних префиксом l:

// call far 0007:00000000 lcall $7,$0 // retf 10 lret $10

Если команда имеет несколько операндов, операнд-источник всегда записывается первым, а приемник — последним, то есть в точности наоборот по сравнению с Intel-синтаксисом:

// mov ax,bx movw %bx,%ax // imul eax.ecx,16 imull $16,%ecx,%eax

Все префиксы имеют имена, которыми они задаются как обычные команды, — перед командой, для которой данный префикс предназначен. Имена префиксов замены сегмента — segcs, segds, segss, segfs, seggs, имена префиксов изменения разрядности адреса и операнда- addr16 и data 16:

segfs movl variable,%eax rep stosd

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

movl %fs:variable, %eax



Глоссарий


Активационная запись (activation record) — область стека, заполняемая при вызове процедуры

Ассемблер (assembly language) — язык программирования низкого уровня

Ассемблер (assembler) — компилятор с языка ассемблера

Байт (byte) — тип данных, имеющий размер 8 бит, минимальная адресуемая единица памяти

Бит (bit) — минимальная единица измерения информации

«Всплывающая» программа (popup program) — резидентная программа, активирующаяся по нажатию определенной «горячей» клавиши

«Горячая» клавиша (hotkey) — клавиша или комбинация клавиш, используемая не для ввода символов, а для вызова программ и подобных необычных действий

Двойное слово (double word) — тип данных, имеющий размер 32 бита

Дескриптор (descriptor) — восьмибайтная структура, хранящаяся в одной из таблиц GDT, LDT или IDT и описывающая сегмент или шлюз

Директива (directive) — команда ассемблеру, которая не соответствует командам процессора

Драйвер (driver) — служебная программа, выполняющая функции посредника между операционной системой и внешним устройством

Защищенный режим (protected mode) — режим процессора, в котором действуют механизмы защиты, сегментная адресация с дескрипторами и селекторами и страничная адресация

Задача (task) — программа, модуль или другой участок кода программы, который можно запустить, выполнять, отложить и завершить

Идентификатор (handle или identifier) — число (если handle) или переменная другого типа, используемая для идентификации того или иного ресурса

Исключение (exception) — событие, при котором выполнение программы прекращается и управление передается обработчику исключения

Код (code) — исполнимая часть программы (обычная программа состоит из кода, данных и стека)

Компилятор (compiler) — программа, преобразующая текст, написанный на понятном человеку языке программирования, в исполнимый файл


Конвейер (pipe) — последовательность блоков процессора, которая задействуется при выполнении команды

Конвенция (convention) — договоренность о передаче параметров между процедурами

Конечный автомат (finite state machine) — программа, которая может переключаться между различными состояниями и выполнять в разных состояниях разные действия

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

Лимит (limit) — поле дескриптора (равно размеру сегмента минус 1)

Линейный адрес (linear address) — адрес, получаемый сложением смещения и базы сегмента

Ловушка (trap) — исключение, происходящее после вызвавшей его команды

Метка (label) — идентификатор, связанный с адресом в программе

Нить (thread) — процесс, данные и код которого совпадают с данными и кодом других процессов

Нереальный режим (unreal mode) — реальный режим с границами сегментов по 4 Гб

Операнд (operand) — параметр, передаваемый команде процессора

Описатель носителя (media descriptor) — байт, используемый DOS для идентификации типа носителя (обычно не используется)

Останов (abort) — исключение, происходящее асинхронно

Отложенное вычисление (lazy evaluation) — вычисление, которое выполняется, только если реально требуется его результат

Очередь предвыборки (prefetch queue) — буфер, из которого команды передаются на расшифровку и выполнение

Ошибка (fault) — исключение, происходящее перед вызвавшей его командой

Пиксель (pixel) — минимальный элемент растрового изображения

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

Подчиненный сегмент (conforming segment) — сегмент, на который можно передавать управление программам с более низким уровнем привилегий

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



Разворачивание циклов (loop unrolling) — превращение циклов, выполняющихся известное число раз, в линейный участок кода

Реальный режим (real mode) — режим, в котором процессор ведет себя идентично 8086 — адресация не выше одного мегабайта памяти, размер всех сегментов ограничен и равен 64 Кб, только 16-битный режим

Резидентная программа (resident program) — программа, остающаяся в памяти после возврата управления в DOS

Сегмент (segment) — элемент сегментной адресации в памяти или участок программы для DOS/Windows

Селектор (selector) — число, хранящееся в сегментном регистре

Секция (section) — участок программы для UNIX

Скан-код (scan-code) — любой код, посылаемый клавиатурой

Слово (word) — тип данных, имеющий размер 16 бит

Смещение (offset) — относительный адрес, отсчитываемый от начала сегмента

Стековый кадр (stack frame) — область стека, занимаемая параметрами процедуры, активационной записью и локальными переменными или только локальными переменными

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

Таблица переходов (jumptable) — массив адресов процедур для косвенного перехода на процедуру с известным номером

Шлюз (gate) — структура данных, позволяющая осуществлять передачу управления между разными уровнями привилегий в защищенном режиме


Кодировки второй половины ASCII



Рис. 19. Кодировка IBM

Кодировка по умолчанию для первых компьютеров — этот набор символов хранится в постоянной памяти и используется BIOS (рис. 19).


Рис. 20. Кодировка cp866

Кодировка cp866 используется DOS-приложениями как основная кодировка и компьютерной сетью Fidonet как транспортная кодировка (рис. 20).


Рис. 21. Кодировка KOI8-R (RFC 1489)

Кодировка KOI8-R используется как транспортная кодировка в Internet и как основная кодировка в большинстве бесплатно распространяемых операционных систем (рис. 21).


Рис. 22. Кодировка ISO 8859-5

Кодировка ISO 8859-5 используется как основная в большинстве коммерческих UNIX-совместимых операционных систем (рис. 22).


Рис. 23. Кодировка cp1251

Кодировка cp1251 используется как основная в графических приложениях для Microsoft Windows (рис. 23).



Коды символов расширенного ASCII


Таблица 24. Расширенные ASCII-коды

Клавиша Код Клавиша Код Клавиша Код Клавиша Код Клавиша Код
F1 3Bh Alt-R 13h Shift-F11 87h Alt-Tab A5h Alt-I 17h
F2 3Ch Alt-S 1Fh Shift-F12 88h Ctrl-Tab 94h Alt-J 24h
F3 3Dh Alt-T 14h Alt-0 81h Alt-Del A3h Alt-K 25h
F4 3Eh Alt-U 16h Alt-1 82h Alt-End 9Fh Alt-L 26h
F5 3Fh Alt-V 2Fh Alt-2 83h Alt-Home 97h Ctrl-Right 74h
F6 40h Alt-W 11h Alt-3 84h Alt-Ins A2h Ctrl-End 75h
F7 41h Alt-X 2Dh Alt-4 85h Alt-PgUp 99h Ctrl-Home 77h
F8 42h Alt-Y 15h Alt-5 86h Alt-PgDn A1h Ctrl-PgDn 76h
F9 43h Alt-Z 2Ch Alt-6 87h Alt-Enter 1Ch Ctrl-PgUp 84h
F10 44h Alt-\ 2Bh Alt-7 88h Ctrl-F1 5Eh Alt-Up 98h
F11 85h Alt-, 33h Alt-8 89h Ctrl-F2 5Fh Alt-Down A0h
F12 86h Alt-. 34h Alt-9 8Ah Ctrl-F3 60h Alt-Left 9Bh
Alt-F1 68h Alt-/ 35h AltC 8Bh Ctrl-F4 61h Alt-Right 9Dh
Alt-F2 69h Alt-BS 0Eh Alt-= 8Ch Ctrl-F5 62h Alt-K/ A4h
Alt-F3 6Ah Alt-[ 1Ah NUL 03h Ctrl-F6 63h Ctrl-K* 37h
Alt-F4 6Bh Alt-] 1Bh Shift-Tab 0Fh Ctrl-F7 64h Alt-K- 4Ah
Alt-F5 6Ah Alt-; 27h Ins 52h Ctrl-F8 65h Alt-K+ 4Eh
Alt-F6 6Dh Alt-' 28h Del 53h Ctrl-F9 66h Alt-KEnter A6h
Alt-F7 6Eh Alt-` 29h SysRq 72h Ctrl-F10 67h Ctrl-K/ 95h
Alt-F8 6Fh Shift-F1 54h Down 50h Ctrl-F11 89h Ctrl-K* 96h
Alt-F9 70h Shift-F2 55h Left 4Bh Ctrl-F12 8Ah Ctrl-K- 8Eh
Alt-F10 71h Shift-F3 56h Right 4Dh Alt-A 1Eh Ctrl-K+ 90h
Alt-F11 8Bh Shift-F4 57h Up 48h Alt-B 30h Ctrl-K8 8Dh
Alt-F12 8Ch Shift-F5 58h Enter 4Fh Alt-C 2Eh Ctrl-K5 8Fh
Alt-M 32h Shift-F6 59h Home 47h Alt-D 20h Ctrl-K2 91h
Alt-N 31h Shift-F7 5Ah PgDn 51h Alt-E 12h Ctrl-K0 92h
Alt-O 18h Shift-F8 5Bh PgUp 49h Alt-F 21h Ctrl-K. 93h
Alt-P 19h Shift-F9 5Ch Ctrl-Left 73h Alt-G 22h    
Alt-Q 10h Shift-F10 5Dh Alt-Esc 01h Alt-H 23h    

Префикс «K» соответствует клавишам на цифровой клавиатуре.



Команды процессоров Intel 8088– Pentium II


Таблица 27. Команды

Команда Код 8088
8087
80186 80286
80287
80386
80387
80486 P 5 P 6
AAA 37 8 8 3 4 3 3 NP 1m
AAD i8 D5 ib 60 15 14 19 14 10 NP 3m
AAM i8 D4 ib 83 19 16 17 15 18 NP 4m
AAS 3F 8 7 3 4 3 3 NP 1m
ADC ac,im 14w im 4 4 3 2 1 1 PU 2m
ADC r,im 80sw /2im 4 4 3 2 1 1 PU 2m
ADC m,im 80sw /2im 23+ea 16 7 7 3 3 PU 4m
ADC r,r 10dw /r 3 3 2 2 1 1 PU 2m
ADC m,r 10dw /r 24+ea 10 7 7 3 3 PU 4m
ADC r,m 10dw /r 13+ea 10 7 6 2 2 PU 3m
ADD ac,im 04w im 4 4 3 2 1 1 UV 1m
ADD r,im 80sw /0 im 4 4 3 2 1 1 UV 1m
ADD m,im 80sw /0 im 23+ea 16 7 7 3 3 UV 4m
ADD r,r 00dw /r 3 3 2 2 1 1 UV 1m
ADD m,r 00dw /r 24+ea 10 7 7 3 3 UV 4m
ADD r,m 00dw /r 13+ea 10 7 6 2 2 UV 2m
AND ac,im 24w im 4 4 3 2 1 1 UV 1m
AND r,im 80sw /4 im 4 4 3 2 1 1 UV 1m
AND m,im 80sw /4 im 23+ea 16 7 7 3 3 UV 4m
AND r,r 20dw /r 3 3 2 2 1 1 UV 1m
AND m,r 20dw /r 24+ea 10 7 7 3 3 UV 4m
AND r,m 20dw /r 13+ea 10 7 6 2 2 UV 2m
ARPL r,r 63 /r     10 20 9 7 NP C
ARPL m,r 63 /r     11 21 9 7 NP C
BOUND r,m 62 /r   35 13 10 7 8 NP C
BSF r16,r16 0F BC /r       10+3n 6..42 6..34 NP 2m
BSF r32,r32 0F BC /r       10+3n 6..42 6..42 NP 2m
BSF r16,m16 0F BC /r       10+3n 6..43 6..35 NP 3m
BSF r32,m32 0F BC /r       10+3n 6..43 6..43 NP 3m



Общая информация о скоростях выполнения


Скорости выполнения команд для процессоров 8086 – Р5 даны в тактах (когда говорят, что тактовая частота процессора 100 MHz, это означает, что за секунду проходит 100 миллионов тактов).

Для процессоров Р5 (Pentium, Pentium MMX) помимо скорости указано, может ли команда выполняться одновременно с другими, и если да, то в каком конвейере (см. главу 9.3.2):

UV — может выполняться одновременно, в любом конвейере;

PU — может выполняться одновременно, в U-конвейере;

PV — может выполняться одновременно, в V-конвейере;

FX — может выполняться одновременно с командой FXCH;

NP — не может выполняться одновременно (для ММХ — не может выполняться одновременно с командой того же типа, который указан после буквы «n»).

Для процессоров Р6 (Pentium Pro, Pentium II) указано количество микроопераций, на которые декодируется команда. Буквой «С» отмечены команды со сложным строением (см. главу 9.3.3).

Во всех случаях даны минимально возможные скорости — если шина данных не заблокирована, операнды выровнены по границам двойных слов, операнды находятся в кэше данных, команды по адресу для перехода находятся в кэше кода, переходы угаданы процессором правильно, в момент выполнения команды не происходит заполнения кэша, страницы находятся в TLB (иначе для Р5 следует прибавить 13 – 28 тактов), не происходит исключений в момент выполнения команды, не происходят AGI и т.д.

Операнды обозначаются следующим образом:

im — непосредственный операнд;

i8, i16, i32 — непосредственный операнд указанного размера;

ас — ЕАХ, АХ, AL;

r — любой регистр общего назначения;

r8 — АН, AL, BH, BL, DH, DL, CH, CL;

r16 — АХ, ВХ, СХ, DX, ВР, SP, SI, DI;

r32 — ЕАХ, ЕВХ, ЕСХ, EDX, EBP, ESP, ESI, EDI;

sr — сегментный регистр;

m — операнд в памяти;

mm — регистр ММХ;

s0 — регистр ST(0);

si — регистр ST(i).

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

Другие используемые сокращения:

РМ — защищенный режим;

RM — реальный режим;

VM — режим V86;

TS — переключение задачи;

CG — шлюз вызова;

TG — шлюз задачи;

/in — увеличение привилегий;

/out — уменьшение привилегий;

/NT — переход во вложенную задачу.

Время переключения задачи:

Pentium: TS = 85 при переключении в 32-битный и 16-битный TSS и 71 при переключении в V86 TSS;

80486: TS = 199 при переключении в 32-битный и 16-битный TSS и 177 при переключении в V86 TSS;

80386: TS = 307 – 314 при переключении в 32-битный и 16-битный TSS и 224 – 231 при переключении в V86;

80286: JS = 280.


Общий формат команды процессора Intel


Команда может содержать до шести полей:

Префиксы— от нуля до четырех однобайтных префиксов.

Код — один или два байта, определяющие команду.

ModR/M — 1 байт (если он требуется), описывающий операнды:

биты 7 – 6: поле MOD — режим адресации;

биты 5 – 3: поле R/O — либо указывает регистр, либо является продолжением кода команды;

биты 2 – 0: поле R/M — либо указывает регистр, либо совместно с MOD - режим адресации.

SIB — 1 байт, если он требуется (расширение ModR/M для 32-битной адресации):

биты 7 – 6: S — коэффициент масштабирования;

биты 5 – 3: I — индексный регистр;

биты 2 – 0: В — регистр базы.

Смещение — 0, 1, 2 или 4 байта.

Непосредственный операнд — 0, 1, 2 или 4 байта — будем использовать /ib и /iw для указания этих операндов.



Префиксы


Все префиксы выполняются за 1 такт и имеют размер 1 байт:

F0h: LOCK

F2h: REPNE/REPNZ

F3h: REP/REPE/REPZ

2Eh: CS:

36h: SS:

3Eh: DS:

26h: ES:

64h: FS:

65h: GS:

66h: OS

67h: AS



В этом приложении приведены скорости


В этом приложении приведены скорости выполнения всех команд процессоров Intel от 8086 до Pentium II и машинные коды, которые им соответствуют.


Символы ASCII


Номера строк соответствуют первой цифре в шестнадцатеричном коде символа, номера столбцов — второй, так что, например, код большой латинской буквы A — 41h (см. рис. 18).


Рис. 18. Таблица символов ASCII



Скан-коды клавиатуры


Таблица 25. Скан-коды

Клавиша Код Клавиша Код Клавиша Код Клавиша Код
Esc 01h Enter 1Ch K* 37h Ins 52h
1 ! 02h Ctrl 1Dh Alt 38h Del 53h
2 @ 03h A 1Eh SP 39h SysRq 54h
3 # 04h S 1Fh Caps 3Ah Macro 56h
4 $ 05h D 20h F1 3Bh F11 57h
5 % 06h F 21h F2 3Ch F12 58h
6 ^ 07h G 22h F3 3Dh PA1 5Ah
7 & 08h H 23h F4 3Eh F13/LWin 5Bh
8 * 09h J 24h F5 3Fh F14/RWin 5Ch
9 ( 0Ah K 25h F6 40h F15/Menu 5Dh
0 ) 0Bh L 26h F7 41h F16 63h
- _ 0Ch ; : 27h F8 42h F17 64h
= + 0Dh ' " 28h F9 43h F18 65h
BS 0Eh ` ~ 29h F10 44h F19 66h
Tab 0Fh LShift 2Ah Num 45h F20 67h
Q 10h \ | 2Bh Scroll 46h F21 68h
W 11h Z 2Ch Home 47h F22 69h
E 12h X 2Dh - 48h F23 6Ah
R 13h C 3Eh PgUp 49h F24 6Bh
T 14h V 2Fh K- 4Ah EraseEOF 6Dh
Y 15h B 30h   4Bh Copy/Play 6Fh
U 16h N 31h K5 4Ch CrSel 72h
I 17h M 32h ® 4Dh Delta 73h
O 18h , < 33h K+ 4Eh ExSel 74h
P 19h . > 34h End 4Fh Clear 76h
[ { 1Ah / ? 35h I 50h    
] } 1Bh RShift 36h PgDn 51h    

Префикс «K» соответствует клавишам на цифровой клавиатуре.

Таблица 26. Служебные скан-коды

Код Функция Код Функция
00h Буфер клавиатуры переполнен FAh ACK
AAh Самотестирование закончено FCh Ошибка самотестирования
E0h Префикс для серых клавиш FDh Ошибка самотестирования
E1h Префикс для клавиш без кода отпускания FEh RESEND
F0h Префикс отпускания клавиши FFh Ошибка клавиатуры
EEh Эхо    



Список используемых сокращений


ABI — Application Binary Interface — интерфейс для приложений на низком уровне

AGI — Address Generation Interlock — задержка для генерации адреса

AMIS — Alternative Multiplex Interrupt Specification — спецификация альтернативного мультиплексорного прерывания

API — Application Program Interface — интерфейс между приложением и программой

ASCII — American Standard Code for Information Interchange — американский стандартный код для обмена информацией

AT&T — American Telephone and Telegraph — американский телефон и телеграф (компания, которой принадлежала торговая марка UNIX)

BCD — Binary Coded Decimal — двоично-десятичный формат

BIOS — Basic Input/Output System — основная система ввода-вывода

BIT — BInary digiT — двоичная цифра

BPB — BIOS Parameter Block — блок параметров BIOS (для блочных устройств)

BRM — Big Real Mode — большой реальный режим (то же, что и нереальный режим)

BSD — Berkeley System Distribution — один из основных видов UNIX-систем

BSS — Block, Started by Symbol — участок программы, содержащий неинициализированные данные

CMOS — Complementary Metal Oxide Semiconductor — комплементарные металлооксидные пары

COFF — Common Object File Format — общий формат объектных файлов

CPL — Current Privilege Level — текущий уровень привилегий

CRT — Cathode Ray Tube — электронно-лучевая трубка

DAC — Digital to Analog Converter — цифро-аналоговый преобразователь

DDK — Drivers Development Kit — набор для создания драйверов

DLL — Dynamically Linked Library — динамическая библиотека

DMA — Direct Memory Access — прямой доступ к памяти


DOS — Disk Operating System — дисковая операционная система

DPL — Descriptor Privilege Level — уровень привилегий дескриптора

DPMI — DOS Protected Mode Interface — интерфейс для защищенного режима в DOS

DSP — Digital Signal Processor — процессор для оцифрованного звука в звуковых картах

DTA — Disk Transfer Area — область передачи дисковых данных (в DOS)

ELF — Executable and Linking Format — формат исполнимых и компонуемых файлов

EMS — Expanded Memory Specification — спецификация доступа к дополнительной памяти

ЕРВ — Execution Program Block — блок информации об исполняемой программе

FAT — File Allocation Table — таблица распределения файлов

FCR — FIFO control register — регистр управления FIFO

FIFO — First In First Out — первый вошел — первый вышел (очередь)

FM — Frequency Modulation — частотный синтез

FPU — Floating Point Unit — блок для работы с числами с плавающей запятой

GDT — Global Descriptor Table — глобальная таблица дескрипторов

HCI — Human Computer Interface — интерфейс между пользователем и программой

HMA — High Memory Area — верхняя область памяти (64 Кб после первого мегабайта)

IBM — International Business Machines — название компании

ICW — Initialization Control Word — управляющее слово инициализации

IDE — Integrated Drive Electronics — один из интерфейсов для жестких дисков

IDT — Interrupt Descriptor Table — таблица дескрипторов обработчиков прерываний

IER — Interrupt Enable Register — регистр разрешения прерываний

IOCTL — Input/Output Contorl — управление вводом-выводом



IOPL — Input/Output Privilege Level — уровень привилегий ввода-вывода

IRQ — Interrupt Request — запрос на прерывание (от внешнего устройства)

ISP — Interrupt Sharing Protocol — протокол разделения прерываний

LCR — Line Control Register — регистр управления линией

LDT — Local Descritor Table — таблица локальных дескрипторов

LE — Linear Executable — линейный исполнимый формат

LFN — Long File Name — длинное имя файла

LSR — Line Status Register — регистр состояния линии

MASM — Macro Assembler — макроассемблер (ассемблер компании Microsoft)

MCR — Modem Control Register — регистр управления модемом

ММХ — Multimedia Extention — расширение для работы с мультимедийными приложениями

MSR — Modem State Register — регистр состояния модема

MSR — Machine Specific Register — машинно-специфичный регистр

NE — New Executable — новый исполнимый формат

NPX — Numerical Processor Extention — расширение для работы с числами с плавающей запятой

OCW — Operation Control Word — управляющее слово (для контроллера прерываний)

РЕ — Portable Executable — переносимый исполнимый формат

POST — Power On Self Test — самотестирование при включении

PSP — Program Segment Prefix — префикс программного сегмента

RBR — Reciever Buffer Register — регистр буфера приемника

RFC — Request For Comments — запрос для комментария (форма публикации документов в Internet, включая стандарты)

RFM — Real Flat Mode — реальный flat-режим (то же, что и нереальный режим)

RTC — Real Time Clock — часы реального времени



RPL — Requestor Privilege Level — запрашиваемый уровень привилегий

RPN — Reverse Polish Notation — обратная польская запись (для арифметических выражений)

SCSI — Small Computer System Interface — один из интерфейсов для жестких дисков

SUN — Stanford University Networks — название компании

SVGA — Super VGA — любой видеоадаптер, способный на режимы, большие 13h

TASM — Turbo Assembler — турбоассемблер (ассемблер компании Borland)

THR — Transmitter Holding Register — регистр хранения передатчика

TSR — Terminate and Stay Resident — завершиться и остаться резидентным

TSS — Task State Segment — сегмент состояния задачи

UMB — Upper Memory Block — блок верхней памяти (между границами 640 Кб и 1 Мб)

VBE — VESA BIOS Extention — спецификация VESA для расширения BIOS

VCPI — Virtual Control Program Interface — один из интерфейсов к защищенному режиму для DOS

VESA — Video Electronics Standard Association — Ассоциация по стандартизации видео в электронике

VGA — Video Graphics Array — основной тип видеоадаптеров

VxD — Virtual X Device — виртуальное устройство Х (общее название виртуальных драйверов в Windows 95)

WASM — Watcom Assembler — ассемблер компании Watcom

XMS — Extended Memory Specification — спецификация доступа к расширенной памяти


Управляющие символы ASCII


Таблица 23. Управляющие символы ASCII

Код Имя Ctrl-код Назначение
00 NUL ^@ Пусто (конец строки)
01 SOH ^A Начало заголовка
02 STX ^B Начало текста
03 EOT ^C Конец текста
04 ENQ ^D Конец передачи
06 ACK ^F Подтверждение
07 BEL ^G Звонок
08 BS ^H Шаг назад
09 HT ^I Горизонтальная табуляция
0A LF ^J Перевод строки
0B VT ^K Вертикальная табуляция
0C FF ^L Перевод страницы
0D CR ^M Возврат каретки
0E SO ^N Выдвинуть
0F SI ^O Сдвинуть
10 DLE ^P Оставить канал данных
11 DC1/XON ^Q Управление устройством 1
12 DC2 ^R Управление устройством 2
13 DC3/XOFF ^S Управление устройством 3
14 DC4 ^T Управление устройством 4
15 NAK ^U Отрицательное подтверждение
16 SYN ^V Синхронизация
17 ETB ^W Конец блока передачи
18 CAN ^X Отмена
19 EM ^Y Конец носителя
1A SUB ^Z Замена
1B ESC ^[ Escape
1C FS ^\ Разделитель файлов
1D GS ^] Разделитель групп
1E RS ^^ Разделитель записей
1F US ^_ Разделитель полей
20 SP   Пробел
7F DEL ^? Удаление



Значения полей кода команды


В кодах некоторых команд мы будем встречать специальные биты и группы бит, которые обозначим w, s, d, reg, sreg и cond:

w = 0, если команда работает с байтами;

w = 1, если команда работает со словами или двойными словами;

s = 0, если непосредственный операнд указан полностью;

s = 1, если непосредственный операнд — младший байт большего операнда и должен рассматриваться как число со знаком;

d = 0, если код источника находится в поле R/O, а приемника — в R/M;

d = 1, если код источника находится в поле R/M, а приемника — в R/O.

Запись 10dw будет означать, что код команды — 000100dw.

Поле reg определяет используемый регистр и имеет длину 3 бита:

000 — AL/AX/EAX/ST(0)/MM0

001 — CL/CX/ECX/ST(1)/MM1

010 — DL/DX/EDX/ST(2)/MM2

011 — BL/BX/EBX/ST(3)/MM3

100 — AH/SP/ESP/ST(4)/MM4

101 — CH/BP/EBP/ST(5)/MM5

110 — DH/SI/ESI/ST(6)/MM6

111 — BH/DI/EDI/ST(7)/MM7

Запись C8r будет означать 11001reg.

Поле sreg определяет используемый сегментный регистр:

000 — ES

001 — CS

010 — SS

011 — DS

100 — FS

110 — GS

Поле cond определяет условие для команд Jcc, CMOVcc, SETcc, FCMOVcc.

Его значения для разных команд:

0000 — О

0001 — NO

0010 — C/B/NAE

0011 — NC/NB/AE

0100 — E/Z

0101 — NE/NZ

0110 — BE/NA

0111 — NBE/A

1000 — S

1001 — NS

1010 — Р/РЕ

1011 — NP/PO

1100 — L/NGE

1101 — NL/GE

1110 — LE/NG

1111 — LNE/G

Запись типа 4сс будет означать 0100cond.



Значения поля ModRM


Поле R/O (биты 5– 3) содержит либо дополнительные три бита кода команды, либо код операнда, который может быть только регистром. Будем обозначать второй случай reg, а в первом записывать используемые биты.

Поля MOD (биты 7 – 6) и R/M (биты 3 – 0) определяют операнд, который может быть как регистром, так и переменной в памяти:

MOD = 11, если используется регистровая адресация и R/M содержит код регистра reg;

MOD = 00, если используется адресация без смещения ([ВХ + SI] или [EDX]);

MOD = 01, если используется адресация с 8-битным смещением (variable[BX + SI]);

MOD = 10, если используется адресация с 16- или 32-битным смещением.

Значение поля R/M различно в 16- и 32-битных режимах.

R/M в 16-битном режиме:

000 — [ВХ + SI]

001 — [ВХ + DI]

010 — [BP + SI]

011 — [ВР + DI]

100 — [SI]

101 — [DI]

110 — [ВР] (кроме MOD = 00 — в этом случае после ModR/M располагается 16-битное смещение, то есть используется прямая адресация)

111 — [ВХ]

R/M в 32-битном режиме:

000 — [ЕАХ]

001 — [ЕСХ]

010 — [EDX]

011 — [ЕВХ]

100 — используется SIB

101 — [ЕВР] (кроме MOD = 00 — в этом случае используется SIB, после которого располагается 32-битное смещение)

110 — [ESI]

111 — [EDI]



Значения поля SIB


Значение поля S:

00 — не используется;

01 — умножение на 2;

10 — умножение на 4;

11 — умножение на 8;

Значения полей I и В:

(I — регистр, используемый в качестве индекса, то есть умножающийся на S, В — регистр базы, который не умножается)

000 — ЕАХ

001 — ЕСХ

010 — EDX

011 — ЕВХ

100 — для I — индекса нет
   для В — ESP

101 — для I — ЕВР
   для В — ЕВР, только если MOD = 01 или 10, если MOD = 00 — базы нет

110 — ESI

111 — EDI

Поля ModR/M и SIB будут записываться как /r, если поле R/O содержит код регистра, или /0 – /7, если поле R/O содержит дополнительные три бита кода команды. В других случаях поля ModR/M и SIB отсутствуют только у команд без операндов, так что они не будут обозначаться дополнительно.