Сколько команд в процессоре intel
Процессор является основой любого компьютера. Это большая микросхема, содержащая внутри себя сотни тысяч или даже миллионы элементов. Современные процессоры чрезвычайно сложны и могут содержать несколько уровней построения и описания. Так, можно различать внешние команды процессора в том виде, в котором они используются в программах и записываются в оперативной памяти, и внутренний микрокод, применяемый для реализации внешних команд . Процессор может содержать внутри себя устройства, предназначенные для ускорения работы, — конвейер команд, устройство опережающей выборки из памяти, кеш-память и т.п.
Рассмотрим лишь самые общие принципы построения и работы процессора, которые одинаковы как для примитивных, так и для самых современных процессоров.
Любой процессор имеет устройство, выполняющее команды, и собственную внутреннюю память , реализованную внутри микросхемы процессора. Она называется регистрами процессора. Имеется 3 типа регистров:
- общие регистры хранят целые числа или адреса. Размер общего регистра совпадает с размером машинного слова и в 32-разрядной архитектуре равен четырем байтам. Число общих регистров и их назначение зависит от конкретного процессора. В большинстве Ассемблеров к ним можно обращаться по именам R0 , R1 , R2 , . Среди общих регистров имеются регистры специального назначения: указатель стека SP ( Stack Pointer ), счетчик команд PC ( Program Counter ) и др.;
- регистр флагов содержит биты, которые устанавливаются в единицу или в ноль в зависимости от результата выполнения последней команды. Так, бит Z устанавливается в единицу, если результат равен нулю (Zero), бит N — если результат отрицательный (Negative), бит V — если произошло переполнение (oVerflow), бит С - если произошел перенос единицы из старшего или младшего разряда ( Carry ), например, при сложении двух целых чисел или при сдвиге. Значения битов в регистре флагов используются в командах условных переходов;
- плавающие регистры содержат вещественные числа. В простых процессорах аппаратная поддержка арифметики вещественных чисел может отсутствовать. В этом случае плавающих регистров нет, а операции с вещественными числами реализуются программным путем.
Команды, или инструкции, процессора состоят из кода операции и операндов. Команда может вообще не иметь операндов или иметь один, два, три операнда. Команды с числом операндов большим трех встречаются лишь в процессорах специального назначения (служащих, например, для обработки сигналов) и в обычных архитектурах не используются. Чаще всего применяются двухадресные и трехадресные архитектуры: к двухадресным относятся, к примеру, все процессоры серии Intel 80x86 , к трехадресным — серии Motorola 68000. В двухадресной архитектуре команда сложения выглядит следующим образом:
т.е. один из аргументов команды является одновременно и ее результатом. Этот аргумент называется получателем (destination). Аргумент , который не меняется в результате выполнения команды, называется источником (source). Среди программистов нет единого мнения о том, в каком порядке записывать аргументы при использовании Ассемблера, т.е. в символической записи машинных команд. Например, в Ассемблере " masm " фирмы IBM для процессоров Intel 80x86 получатель всегда записывается первым, а источник вторым. Ассемблер " masm " используется в операционных системах MS DOS и Windows . В Ассемблере " as ", который входит в состав компилятора "gcc" и используется в системах типа Unix (Linux и т.п.), получатель всегда является последним аргументом. Та же команда сложения записывается в " as " как
что означает сложить Y и X и результат записать в X .
В трехадресной архитектуре команда сложения имеет 3 операнда:
Получателем в трехадресной архитектуре обычно является третий аргумент , т.е. в данном случае сумма X+Y записывается в Z .
Операндами команды могут быть регистры или элементы памяти. В действительности, конечно, процессор всегда сначала копирует слово из памяти в регистр , который может быть либо явно указан в команде, либо использоваться неявно. Операция всегда выполняется с содержимым регистров. После этого результат может быть записан в память либо оставлен в регистре. Например, при выполнении команды увеличения целого числа на единицу
в случае, когда операнд X является словом оперативной памяти, содержимое слова X сначала неявно копируется во внутренний регистр процессора, затем выполняется его увеличение на единицу, и после этого увеличенное значение записывается обратно в память .
Имеется несколько способов задания операнда, находящегося в оперативной памяти, они называются режимами адресации. Это
- абсолютная адресация - когда в команде указывается константа, равная адресу аргумента ;
- косвенная адресация - когда в команде указывается регистр, содержащий адрес аргумента ;
- относительная адресация - адрес аргумента равен сумме содержимого регистра и константы, задающей смещение;
- индексная адресация с масштабированием - адрес аргумента равен сумме содержимого базового регистра, константы, задающей смещение, а также содержимого индексного регистра, умноженного на масштабирующий множитель. Масштабирующий множитель может принимать значения 1, 2, 4, 8. Этот режим удобен для обращения к элементу массива.
Бывают и другие, более изощренные, режимы адресации, когда, например, адрес аргумента содержится в слове, адрес которого содержится в регистре (так называемая двойная косвенность).
CISC и RISC-процессоры
Существует два подхода к конструированию процессоров. Первый состоит в том, чтобы придумать как можно больше разных команд и предусмотреть как можно больше разных режимов адресации. Процессоры такого типа называются CISC-процессорами, от слов Сomplex Instruction Set Computers. Это, в частности, Intel 80x86 и Motorola 68000. Противоположный подход состоит в том, чтобы реализовать лишь минимальное множество команд и режимов адресации, процессоры такого типа называются RISC-процессорами, от слов Reduced Instruction Set Computers. Примеры RISC-процессоров: DEC Alpha, Power PC, Intel Itanium .
Казалось бы, CISC-процессоры должны иметь преимущество перед RISC-процессорами, но на самом деле все обстоит строго наоборот. Дело в том, что простота набора команд процессора облегчает его конструирование, в результате чего удается достичь следующих целей:
- все команды выполняются исключительно быстро, причем за одинаковое время, т.е. за фиксированное число тактов работы процессора;
- значительно поднимается тактовая частота процессора;
- намного увеличивается количество регистров процессора и объем кеш -памяти;
- удается добиться ортогональности режимов адресации, набора команд и набора регистров. Это означает, что нет каких-либо выделенных регистров или режимов адресации: в любых (или почти любых) командах можно использовать произвольные регистры и режимы адресации независимо друг от друга. Следует отметить, что к памяти могут обращаться лишь команды загрузки слова из памяти в регистр и записи из регистра в память, а все арифметические команды работают только с регистрами;
- простота команд позволяет эффективно организовать их выполнение в конвейере ( pipeline ), что значительно ускоряет работу программы.
Пункты 3 и 4 по достоинству оценят те, кому пришлось программировать на Ассемблере Intel 80x86 , имеющем ряд ограничений на использование регистров и режимы адресации, к тому же и регистров в нем очень мало.
RISC-архитектуры обладают неоспоримыми преимуществами по сравнению с CISC-архитектурами — быстродействием, низкой стоимостью, удобством программирования и т.д. — и практически не имеют недостатков. Существование CISC-процессоров в большинстве случаев объясняется лишь традицией и требованием совместимости со старым программным обеспечением. Впрочем, существует и третий вариант — процессоры, которые по сути являются RISC-процессорами, но эмулируют внешнюю систему команд устаревших процессоров, например, современные процессоры Intel Pentium.
Алгоритм работы компьютера
Среди всех регистров процессора в любой архитектуре всегда имеется два выделенных регистра: это регистр PC, что означает Program Counter , по-русски его называют счетчиком команд, и регистр SP — Stack Pointer , т.е. указатель стека. Иногда регистр PC обозначают как IP, что означает Instruction Pointer , указатель инструкции. (Команды процессора часто называют инструкциями.)
В фон-Неймановской архитектуре , по которой построены все современные компьютеры, программа, состоящая из машинных команд, содержится в оперативной памяти. Регистр PC всегда содержит адрес команды, которая будет выполняться на следующем шаге. Алгоритм работы процессора выглядит следующим образом:
В простейшем случае, когда выполняется линейный участок программы, команды выбираются из памяти и выполняются последовательно, а содержимое регистра PC монотонно возрастает. Выполнение команды, однако, может приводить к изменению регистра PC . Таким образом организуются безусловные и условные переходы в программе, нарушающие последовательный порядок выполнения команд. С помощью команд условных и безусловных переходов реализуются конструкции ветвления и цикла. Команда перехода представляет собой либо прибавление константы к содержимому PC (константа может быть положительной или отрицательной), либо загрузку в PC адреса элемента памяти со всеми возможными режимами адресации. Первый способ используется для реализации переходов внутри подпрограммы (внутри функции в терминах языка Си), второй -- для перехода к подпрограмме. Впрочем, гораздо чаще в последнем случае используется команда call вызова подпрограммы, которая дополнительно запоминает точку возврата в регистре или в аппаратном стеке.
Как известно, программирование на Ассемблере - это написание исходных текстов, которые представляют собой набор команд (инструкций) процессора. В этом разделе публикуются подробные описания инструкций процессоров Интел и совместимых.
Я уже не раз об этом говорил, но снова повторю - вдруг кто не слышал )))
Каждый процессор имеет свой набор команд (инструкций)!
Поэтому, если вы изучите инструкции одного процессора, то это не значит, что вы легко сможете создавать программы на языке Ассемблера. Потому что язык Ассемблера одинаков для всех процессоров (ну почти одинаков). Однако инструкции, используемые в языке Ассемблера, могут быть (и так оно и есть) отличаться в зависимости от того, для какого процессора вы пишите программу.
Кроме того, команды с одинаковым именем могут по разному работать с разными процессорами.
Тем не менее, изучать то язык как-то надо. Поэтому обычно начинают с каких-то основ. Как правило, с изучения основных инструкций, которые очень похожи для большинства процессоров (и микроконтроллеров в том числе).
Набор базовых команд процессора
Именно с базовых инструкций лучше всего начать изучать Ассемблер. Такими базовыми командами (инструкциями) являются команды сложения, вычитания и других простых математических операций. А также команды перемещения значения из одного источника в другой (например, из области памяти в регистр).
Пример инструкций процессора Интел (8086): ADD (сложение), SUB (вычитание), MOV (перемещение) и т.п.
Базовым набором команд процессора Intel можно считать полный набор инструкций процессора 8086, у которого было 116 команд. У современных процессоров команд, конечно, намного больше (хотя это зависит от архитектуры - есть процессоры с сокращённым набором команд, где их всего несколько десятков).
Современные процессоры кроме основных команд имеют ещё и разные расширения, такие как набор команд MMX, которые предназначены для более быстрого выполнения определённых операций.
Вообще это тема очень объёмная и довольно сложная. Поэтому в очередной раз советую вам изучить (причём очень тщательно) какую-нибудь хорошую книгу по Ассемблеру (на этом сайте есть ссылки на такие книги).
В настоящее время в ЭВМ типа IBM РС используются более 1000 различных команд. По числу адресов команды делятся на безадресные, одноадресные и двухадресные.
- в безадресных командах процессор не выполняет каких-либо операций с данными, т.е. с операндами. Команда содержит только код операции (КОП) и занимает все 16 разрядов, например,
HLT- останов процессора;
WAIT- ожидание прерываний;
- одноадресные команды содержат КОП и один операнд источника или приемника (КОП dd, КОП ss), например,
INC AX- инкремент, увеличить на единицу, АХ (АХ=: АХ+1);
DEC BX- декремент, уменьшить на единицу, ВХ (ВХ=: ВХ-1);
JMP m1 - переход к команде с меткой (адресом ) m1
- двухадресные команды содержат код операции, операнд приёмника и операнд источника (КОП dd, ss), например,
MOV AX, BX- пересылка содержимого регистра ВХ в регистр АХ.
CMP AX,104 -сравнить содержимое регистра AX с значением 104
ROL BX, 3 - содержимое регистра BX сдвинуть влево на 3 разряда
Ниже приводятся наиболее употребимые команды. Разумеется, что программирование на языке Ассемблера невозможно без знания системы команд.
При рассмотрении программ следует четко представлять действия процессора с операндами, входящими в команду.
Представления о данных, их размерах, их расположении в аппаратуре ЭВМ является необходимым для грамотного и успешного проектирования систем управления электроприводами и электроавтоматикой. Процесс компьютерного выполнения задачи можно сравнить с мастером, который по картинке создает мозаику. Картинка - это программа, которую следует воссоздать в мозаике , она перед глазами мастера, выполняющего роль процессора. Он по картинке выбирает из подходящей кучки (сегмент данных) камешек заданного цвета (операнд) укладывает на мозаичное полотно. Структура данных, ее четкое и удачное формирование предшествует программному коду.
ADC(Addition with Carry) - Сложение с переносом
- сложить два операнда;
- поместить результат в первый операнд: приемник = приемник+источник;
- в зависимости от результата установить флаги.
ADD(ADDition) - Сложение
- сложить операнды источник и приемник;
- записать результат сложения в приемник;
- установить флаги.
AND(logical AND) - Логическое И
- выполнить операцию логического умножения над операндами источник и приемник: каждый бит результата равен 1, если соответствующие биты операндов равны 1, в остальных случаях бит результата равен 0;
- записать результат операции в приемник;
- установить флаги.
BT(Bit Test) - Проверка битов
- получить бит по указанному номеру позиции в операнде источник;
- установить флаг согласно значению этого бита.
CALL(CALL) - Вызов процедуры или задачи
· в стек заносится содержимое указателя команд ip и в этот же регистр загружается новое значение адреса, соответствующее метке;
· r16 или m16 — определяют регистр или ячейку памяти, содержащие смещения в текущем сегменте команд, куда передается управление. При передаче управления в стек заносится содержимое указателя команд ip;
CMP(CoMPare operands) - Сравнение операндов
- выполнить вычитание (операнд1-операнд2);
- в зависимости от результата установить флаги, операнд1 и операнд2 не изменять (то есть результат не запоминать).
DEC (DECrement operand by 1) - Уменьшение операнда на единицу
Алгоритм работы: команда вычитает 1 из операнда.
DIV(DIVide unsigned) - Деление беззнаковое
Для команды необходимо задание двух операндов — делимого и делителя. Делимое задается неявно и размер его зависит от размера делителя, который указывается в команде:
- если делитель размером в байт, то делимое должно быть расположено в регистре ax. После операции частное помещается в al, а остаток — в ah;
- если делитель размером в слово, то делимое должно быть расположено в паре регистров dx:ax, причем младшая часть делимого находится в ax. После операции частное помещается в ax, а остаток — в dx;
HLT(HaLT) - Остановка
Алгоритм работы: перевод микропроцессора в состояние остановки.
IN (INput operand from port) -Ввод операнда из порта
Передает байт, слово из порта ввода-вывода в один из регистров al/ax.
INC(INCrement operand by 1) -Увеличить операнд на 1
Алгоритм работы: команда увеличивает операнд на единицу.
INT(INTerrupt) - Вызов подпрограммы обслуживания прерывания
- записать в стек регистр флагов flags и адрес возврата. При записи адреса возврата вначале записывается содержимое сегментного регистра cs, затем содержимое указателя команд ip;
- сбросить в ноль флаги i и t;
- передать управление на программу обработки прерывания с указанным номером. Механизм передачи управления зависит от режима работы микропроцессора.
JCC (Jump if condition) - Переход, если выполнено условие
Проверка состояния флагов в зависимости от кода операции (оно отражает проверяемое условие):
- если проверяемое условие истинно, то перейти к ячейке, обозначенной операндом;
- если проверяемое условие ложно, то передать управление следующей команде.
Команды условного перехода удобно применять для проверки различных условий, возникающих в ходе выполнения программы. Как известно, многие команды формируют признаки результатов своей работы в регистре flags. Это обстоятельство и используется командами условного перехода для работы. Ниже приведены перечень команд условного перехода, анализируемые ими флаги и соответствующие им логические условия перехода.
Команда | Условие перехода |
JA | если выше |
JAE | если выше или равно |
JB | если ниже |
JBE | если ниже или равно |
JC | если перенос |
JZ | если 0 |
JG | если больше |
JGE | если больше или равно |
JL | если меньше |
JLE | если меньше или равно |
JNA | если не выше |
JNAE | если не выше или равно |
JNB | если не ниже |
JNBE | если не ниже или равно |
JNC | если нет переноса |
JNE | если не равно |
JNG | если не больше |
JNGE | если не больше или равно |
JNL | если не меньше |
JNLE | если не меньше или равно |
JNO | если нет переполнения |
JNP | если количество единичных битов результата нечетно (нечетный паритет) |
JNS | если знак плюс (знаковый (старший) бит результата равен 0) |
JNZ | если нет нуля |
JO | если переполнение |
JP | если количество единичных битов результата четно (четный паритет) |
JPE | то же, что и JP, то есть четный паритет |
JPO | то же, что и JNP |
JS | если знак минус (знаковый (старший) бит результата равен 1) |
JZ | если ноль |
Логические условия "больше" и "меньше" относятся к сравнениям целочисленных значений со знаком, а "выше и "ниже" — к сравнениям целочисленных значений без знака. Если внимательно посмотреть, то у многих команд можно заметить одинаковые значения флагов для перехода. Это объясняется наличием нескольких ситуаций, которые могут вызвать одинаковое состояние флагов. В этом случае с целью удобства ассемблер допускает несколько различных мнемонических обозначений одной и той же машинной команды условного перехода. Эти команды ассемблера по действию абсолютно равнозначны, так как это одна и та же машинная команда. Для реализации межсегментных переходов необходимо комбинировать команды условного перехода и команду безусловного перехода jmp. При этом можно воспользоваться тем, что практически все команды условного перехода парные, то есть имеют команды, проверяющие обратные условия.
JMP(JuMP) - Переход безусловный
Команда jmp в зависимости от типа своего операнда изменяет содержимое либо только одного регистра ip, либо обоих регистров cs и ip:
- если операнд в команде jmp — метка в текущем сегменте команд (a8, 16), то ассемблер формирует машинную команду, операнд которой является значением со знаком, являющимся смещением перехода относительно следующей за jmp команды. При этом виде перехода изменяется только регистр ip;
- если операнд в команде jmp — символический идентификатор ячейки памяти, то ассемблер предполагает, что в ней находится адрес, по которому необходимо передать управление. Этот адрес может быть трех видов:
-значением абсолютного смещения метки перехода относительно начала сегмента кода;
-дальним указателем на метку перехода в реальном и защищенном режимах, содержащим два компонента адреса — сегментный и смещение;
-адресом в одном из 16 -разрядных регистров — этот адрес представляет собой абсолютное смещение метки, на которую необходимо передать управление, относительно начала сегмента команд.
Для понимания различий механизмов перехода в реальном и защищенном режимах нужно помнить следующее. В реальном режиме микропроцессор просто изменяет cs и ip в соответствии с содержимым указателя в памяти. В защищенном режиме микропроцессор предварительно анализирует байт прав доступа AR в дескрипторе, номер которого определяется по содержимому сегментной части указателя. В зависимости от состояния байта AR микропроцессор выполняет либо переход, либо переключение задач.
LOOP (LOOP control by register cx) - Управление циклом по cx
- выполнить декремент содержимого регистра cx;
- анализ регистра cx:
- если cx=0, передать управление следующей за loop команде;
- если cx=1, передать управление команде, метка которой указана в качестве операнда loop.
MOV(MOVe operand) - Пересылка операнда
Алгоритм работы: копирование второго операнда в первый операнд.
MUL(MULtiply) - Умножение целочисленное без учета знака
Команда выполняет умножение двух операндов без учета знаков. Алгоритм зависит от формата операнда команды и требует явного указания местоположения только одного сомножителя, который может быть расположен в памяти или в регистре. Местоположение второго сомножителя фиксировано и зависит от размера первого сомножителя:
- если операнд, указанный в команде — байт, то второй сомножитель должен располагаться в al;
- если операнд, указанный в команде — слово, то второй сомножитель должен располагаться в ax;
Результат умножения помещается также в фиксированное место, определяемое размером сомножителей:
- при умножении байтов результат помещается в ax;
- при умножении слов результат помещается в пару dx:ax;
NEG(NEGate operand) - Изменить знак операнда
- выполнить вычитание (0 – источник) и поместить результат на место источника;
- если источник=0, то его значение не меняется.
NOP (No OPeration) - Нет операции
Алгоритм работы: не производит никаких действий.
NOT(NOT operand) - Инвертирование операнда
Алгоритм работы: инвертировать все биты операнда источника: из 1 в 0, из 0 в 1.
OR (logical OR) - Логическое включающее ИЛИ
- выполнить операцию логического ИЛИ над битами операнда назначения, используя в качестве маски второй операнд — маска. При этом бит результата равен 0, если соответствующие биты операндов маска и назначения равны 0, в противном случае бит равен 1;
- записать результат операции в источник (операнд маска остается неизменным);
- установить флаги.
OUT(OUT operand to port) -Вывод операнда в порт
Передать байт, слово, из регистра al/ax в порт, номер которого определяется первым операндом.
POP (POP operand from the stack) - Извлечение операнда из стека
· загрузить в приемник содержимое вершины стека (адресуется парой ss:sp);
· увеличить содержимое sp на 2 байта для use16.
PUSH (PUSH operand onto stack) - Размещение операнда в стеке
- уменьшить значение указателя стека sp на 2 ;
- записать источник в вершину стека (адресуемую парой ss:sp).
RCL (Rotate operand through Carry flag Left) - Циклический сдвиг операнда влево через флаг переноса.
- сдвиг всех битов операнда влево на один разряд, при этом старший бит операнда становится значением флага переноса c;
- одновременно старое значение флага переноса c вдвигается в операнд справа и становится значением младшего бита операнда;
- указанные выше два действия повторяются количество раз, равное значению второго операнда команды rcl.
RCR (Rotate operand through Carry flag Right) - Циклический сдвиг операнда вправо через флаг переноса.
- сдвиг всех битов операнда вправо на один разряд; при этом младший бит операнда становится значением флага переноса c;
- одновременно старое значение флага переноса — в операнд слева и становится значением старшего бита операнда;
- указанные выше два действия повторяются количество раз, равное значению второго операнда команды rcr.
RET(RETurn from procedure)Возврат из процедуры
Алгоритм работы: восстановить из стека содержимое ip;
ROL(Rotate operand Left) - Циклический сдвиг операнда влево
- сдвиг всех битов операнда влево на один разряд, при этом старший бит операнда вдвигается в операнд справа и становится значением младшего бита операнда;
- одновременно выдвигаемый бит становится значением флага переноса c;
- указанные выше два действия повторяются количество раз, равное значению второго операнда.
ROR- Циклический сдвиг операнда вправо. ASCII-коррекция после сложения
- сдвиг всех битов операнда вправо на один разряд, при этом младший бит операнда вдвигается в операнд слева и становится значением старшего бита операнда;
- одновременно этот младший бит операнда становится значением флага переноса c;
- старое значение флага переноса c вдвигается в операнд слева и становится значением старшего бита операнда;
- указанные выше два действия повторяются количество раз, равное значению второго операнда.
SAL (Shift Arithmetic operand Left) - Сдвиг арифметический операнда влево.
- сдвиг всех битов операнда влево на один разряд, при этом выдвигаемый слева бит становится значением флага переноса c;
- одновременно справа в операнд вдвигается нулевой бит;
- указанные выше два действия повторяются количество раз, равное значению второго операнда.
SAR (Shift Arithmetic operand Right) - Сдвиг арифметический операнда вправо.
- сдвиг всех битов операнда вправо на один разряд, при этом выдвигаемый справа бит становится значением флага переноса c;
- одновременно слева в операнд вдвигается не нулевой бит, а значение старшего бита операнда, то есть по мере сдвига вправо освобождающиеся места заполняются значением знакового разряда. По этой причине этот тип сдвига и называется арифметическим;
- указанные выше два действия повторяются количество раз, равное значению второго операнда.
SBB(SuBtract with Borrow) -Вычитание с заемом.
- выполнить сложение операнд_2=операнд_2+(c);
- выполнить вычитание операнд_1=операнд_1-операнд_2;
SHL (SHift logical Left) - Сдвиг логический операнда влево.
- сдвиг всех битов операнда влево на один разряд, при этом выдвигаемый слева бит становится значением флага переноса c;
- одновременно слева в операнд вдвигается нулевой бит;
- указанные выше два действия повторяются количество раз, равное значению второго операнда.
SHR - Сдвиг логический операнда вправо. ASCII-коррекция после сложения
- сдвиг всех битов операнда вправо на один разряд; при этом выдвигаемый справа бит становится значением флага переноса c;
- одновременно слева в операнд вдвигается нулевой бит;
- указанные выше два действия повторяются количество раз, равное значению второго операнда.
SUB(SUBtract) - Вычитание.
- выполнить вычитание операнд_1=операнд_2-операнд_1;
- установить флаги.
TEST(TEST operand) -Логическое И.
- выполнить операцию логического умножения над операндами приемник и источник: бит результата равен 1, если соответствующие биты операндов равны 1, в остальных случаях бит результата равен 0; Результат отражен во флаге ZF
- установить флаги.
XOR- Логическое исключающее ИЛИ. ASCII-коррекция после сложения
- выполнить операцию логического исключающего ИЛИ над операндами: бит результата равен 1, если значения соответствующих битов операндов различны, в остальных случаях бит результата равен 0;
- записать результат сложения в приемник;
- установить флаги.
1.В.Юров Ассемблер, Питер, 2001.
2.Мартыничев А.К. Программное обеспечение компьютерных систем управления оборудованием, Чебоксары, 2010.
3.Джордейн Р. Справочник программиста персональных компьютеров типа IBM PC, XT, AT :пер. с англ. М.: Финансы и статистика,1992. 544 с.: ил.
Романтизм как литературное направление: В России романтизм, как литературное направление, впервые появился .
3.3. Система команд процессора
В общем случае система команд процессора включает в себя следующие четыре основные группы команд:
- команды пересылки данных;
- арифметические команды ;
- логические команды ;
- команды переходов .
Команды пересылки данных не требуют выполнения никаких операций над операндами. Операнды просто пересылаются (точнее, копируются) из источника (Source) в приемник ( Destination ). Источником и приемником могут быть внутренние регистры процессора, ячейки памяти или устройства ввода/вывода. АЛУ в данном случае не используется.
Арифметические команды выполняют операции сложения, вычитания, умножения, деления, увеличения на единицу (инкрементирования), уменьшения на единицу (декрементирования) и т.д. Этим командам требуется один или два входных операнда. Формируют команды один выходной операнд .
Логические команды производят над операндами логические операции, например, логическое И, логическое ИЛИ , исключающее ИЛИ, очистку, инверсию, разнообразные сдвиги (вправо, влево, арифметический сдвиг, циклический сдвиг). Этим командам, как и арифметическим , требуется один или два входных операнда, и формируют они один выходной операнд .
Наконец, команды переходов предназначены для изменения обычного порядка последовательного выполнения команд. С их помощью организуются переходы на подпрограммы и возвраты из них, всевозможные циклы, ветвления программ, пропуски фрагментов программ и т.д. Команды переходов всегда меняют содержимое счетчика команд. Переходы могут быть условными и безусловными. Именно эти команды позволяют строить сложные алгоритмы обработки информации.
В соответствии с результатом каждой выполненной команды устанавливаются или очищаются биты регистра состояния процессора ( PSW ). Но надо помнить, что не все команды изменяют все имеющиеся в PSW флаги. Это определяется особенностями каждого конкретного процессора.
У разных процессоров системы команд существенно различаются, но в основе своей они очень похожи. Количество команд у процессоров также различно. Например, у упоминавшегося уже процессора МС68000 всего 61 команда , а у процессора 8086 — 133 команды. У современных мощных процессоров количество команд достигает нескольких сотен. В то же время существуют процессоры с сокращенным набором команд (так называемые RISC-процессоры), в которых за счет максимального сокращения количества команд достигается увеличение эффективности и скорости их выполнения.
Рассмотрим теперь особенности четырех выделенных групп команд процессора более подробно.
3.3.1. Команды пересылки данных
Команды пересылки данных занимают очень важное место в системе команд любого процессора. Они выполняют следующие важнейшие функции:
- загрузка (запись) содержимого во внутренние регистры процессора;
- сохранение в памяти содержимого внутренних регистров процессора;
- копирование содержимого из одной области памяти в другую;
- запись в устройства ввода/вывода и чтение из устройств ввода/вывода.
В некоторых процессорах (например, Т-11) все эти функции выполняются одной единственной командой MOV (для байтовых пересылок — MOVB ) но с различными методами адресации операндов.
В других процессорах помимо команды MOV имеется еще несколько команд для выполнения перечисленных функций. Например, для загрузки регистров могут использоваться команды загрузки, причем для разных регистров — разные команды (их обозначения обычно строятся с использованием слова LOAD — загрузка). Часто выделяются специальные команды для сохранения в стеке и для извлечения из стека ( PUSH — сохранить в стеке, POP — извлечь из стека). Эти команды выполняют пересылку с автоинкрементной и с автодекрементной адресацией (даже если эти режимы адресации не предусмотрены в процессоре в явном виде).
Иногда в систему команд вводится специальная команда MOVS для строчной (или цепочечной) пересылки данных (например, в процессоре 8086). Эта команда пересылает не одно слово или байт, а заданное количество слов или байтов ( MOVSB ), то есть инициирует не один цикл обмена по магистрали, а несколько. При этом адрес памяти, с которым происходит взаимодействие, увеличивается на 1 или на 2 после каждого обращения или же уменьшается на 1 или на 2 после каждого обращения. То есть в неявном виде применяется автоинкрементная или автодекрементная адресация.
В некоторых процессорах (например, в процессоре 8086) специально выделяются функции обмена с устройствами ввода/вывода. Команда IN используется для ввода (чтения) информации из устройства ввода/вывода, а команда OUT используется для вывода (записи) в устройство ввода/вывода. Обмен информацией в этом случае производится между регистром-аккумулятором и устройством ввода/вывода. В более продвинутых процессорах этого же семейства (начиная с процессора 80286) добавлены команды строчного (цепочечного) ввода (команда INS ) и строчного вывода (команда OUTS ). Эти команды позволяют пересылать целый массив (строку) данных из памяти в устройство ввода/вывода ( OUTS ) или из устройства ввода/вывода в память ( INS ). Адрес памяти после каждого обращения увеличивается или уменьшается (как и в случае с командой MOVS ).
Также к командам пересылки данных относятся команды обмена информацией (их обозначение строится на основе слова Exchange ). Может быть предусмотрен обмен информацией между внутренними регистрами, между двумя половинами одного регистра ( SWAP ) или между регистром и ячейкой памяти.
3.3.2. Арифметические команды
Арифметические команды рассматривают коды операндов как числовые двоичные или двоично-десятичные коды. Эти команды могут быть разделены на пять основных групп:
- команды операций с фиксированной запятой (сложение, вычитание, умножение, деление);
- команды операций с плавающей запятой (сложение, вычитание, умножение, деление);
- команды очистки;
- команды инкремента и декремента;
- команда сравнения.
Команды операций с фиксированной запятой работают с кодами в регистрах процессора или в памяти как с обычными двоичными кодами. Команда сложения ( ADD ) вычисляет сумму двух кодов. Команда вычитания ( SUB ) вычисляет разность двух кодов. Команда умножения ( MUL ) вычисляет произведение двух кодов (разрядность результата вдвое больше разрядности сомножителей). Команда деления ( DIV ) вычисляет частное от деления одного кода на другой. Причем все эти команды могут работать как с числами со знаком, так и с числами без знака.
Команды операций с плавающей запятой (точкой) используют формат представления чисел с порядком и мантиссой (обычно эти числа занимают две последовательные ячейки памяти). В современных мощных процессорах набор команд с плавающей запятой не ограничивается только четырьмя арифметическими действиями, а содержит и множество других более сложных команд, например, вычисление тригонометрических функций, логарифмических функций, а также сложных функций, необходимых при обработке звука и изображения.
Команды очистки ( CLR ) предназначены для записи нулевого кода в регистр или ячейку памяти. Эти команды могут быть заменены командами пересылки нулевого кода, но специальные команды очистки обычно выполняются быстрее, чем команды пересылки . Команды очистки иногда относят к группе логических команд , но суть их от этого не меняется.
Команды инкремента (увеличения на единицу, INC ) и декремента (уменьшения на единицу, DEC ) также бывают очень удобны. Их можно в принципе заменить командами суммирования с единицей или вычитания единицы, но инкремент и декремент выполняются быстрее, чем суммирование и вычитание. Эти команды требуют одного входного операнда, который одновременно является и выходным операндом.
Наконец, команда сравнения (обозначается CMP ) предназначена для сравнения двух входных операндов. По сути, она вычисляет разность этих двух операндов, но выходного операнда не формирует, а всего лишь изменяет биты в регистре состояния процессора ( PSW ) по результату этого вычитания. Следующая за командой сравнения команда (обычно это команда перехода ) будет анализировать биты в регистре состояния процессора и выполнять действия в зависимости от их значений (о командах перехода речь идет в разделе 3.3.4). В некоторых процессорах предусмотрены команды цепочечного сравнения двух последовательностей операндов, находящихся в памяти (например, в процессоре 8086 и совместимых с ним).
Поиск по сайту
СТРУКТУРА КОМАНД INTEL 80x86
Победа в борьбе, как правило, достается тому, кто лучше знает противника. Успешно защититься от хакера сможет лишь тот, кто знает не меньше хакера. Каждый уважающий себя хакер должен понимать, как строятся команды, чтобы с легкостью манипулировать им, занимаясь дизассемблированием. Следовательно и тот, кто хочет защитить свою программу от дизассемблирования, не обойдется без понимания того, как процессор интерпретирует команды.
Формат инструкции архитектуры Intel приведен на рис.1. Это схематичное представление. Пока без размерностей и пояснений.
Рис.1. Формат команд процессора х86 фирмы Intel
Кроме поля кода операции все остальные поля являются необязательными, т.е. в одних командах могут присутствовать, а в других нет.
Префикс
На этом же основан один очень любопытный прием противодействия отладчикам, в том числе и знаменитому отладчику-эмулятору Cup386. Рассмотрим, как работает конструкция 0x66:RETN. Казалось бы, раз команда RETN не имеет операндов, то префикс 0x66 можно просто игнорировать. Но, на самом деле, все не так просто. RETN работает с неявным операндом-регистром ip/eip. Именно его и изменяет префикс. Разумеется, в реальном и 16-разрядном режиме указатель команд всегда обрезается до 16 бит, и поэтому, на первый взгляд, возврат сработает корректно. Но стек-то окажется несбалансированным! Из него вместе одного слова взяли целых два! Так нетрудно получить и исключение 0Ch - исчерпание стека.
Любопытно, какой простой, но какой надежный прием. Впрочем, следует признать, что перехват INT 0Ch под операционной системой Windows бесполезен, и, не смотря на все ухищрения, приложение, породившие такое исключение, будет безжалостно закрыто. Однако, в реальном режиме это работает превосходно.
Одно плохо - все эти приемы не работают под Windows и другими операционными системами. Это вызвано тем, что обработка исключения типа "Общее нарушение защиты" всецело лежит на ядре операционной системы, что не позволяет приложениям распоряжаться им по своему усмотрению. Забавно, но в режиме эмуляции MS-DOS некоторые EMS-драйверы ведут себя в этом случае совершенно непредсказуемо. Часто при этом они не генерируют ни исключения 0Сh, ни 0Dh. Это следует учитывать при разработке защит, основанных на приведенных выше приемах.
Обратим внимание так же и на последовательности типа 0x66 0x66 [ххх]. Хотя фирма Intel не гарантирует корректную работу своих процессоров в такой ситуации, но фактически все они правильно интерпретируют такую ситуацию. Иное дело некоторые отладчики и дизассемблеры, которые спотыкаются и начинают некорректно вести себя.
Есть еще один интересный момент связанный с работой декодера микропроцессора.
Декодер за один раз считывает только 16 байт и, если команда "не уместится", то он просто не сможет считать "продолжение" и сгенерирует исключение "Общее нарушение защиты". Однако, иначе ведут себя эмуляторы, которые корректно обрабатывают "длинные" инструкции.
Впрочем, все это очень процессорно-зависимо. Никак не гарантируется сохранение и поддержание этой особенности в будущих моделях, и поэтому злоупотреблять этим не стоит, иначе ваша защита откажется работать.
Префиксы переопределения сегмента могут встречаться перед любой командой, в том числе и не обращающейся к памяти, например, CS:NOP вполне успешно выполнится. А вот некоторые дизассемблеры сбиться могут. К счастью, IDA Pro к ним не относится. Самое интересное, что комбинация
DS: FS: FG: CS: MOV AХ,[100]
работает вполне нормально (хотя это и не гарантируется фирмой Intel). При этом последний префикс в цепочке перекрывает все остальные. Некоторые отладчики, наоборот, ориентируются на первый префикс в цепочке, что дает неверный результат. Этот пример хорош тем, что великолепно выполняется под Windows и другими операционными системами. К сожалению, на декодирование каждого префикса тратится один такт, и все это может медленно работать.Код операции
Само поле кода операции занимает восемь бит и чаще всего имеет следующий формат (рис. 2):
Рис. 2. Формат поля "Код операции"
Поле размера указывает на размер операндов. Это поле равно 0, если операнды имеют размер в один байт. Это поле равно 1, если операнды имеют размер в слово (двойное слово в 32-битном режиме или в 16-битном режиме с префиксом 0x66).
Поле направления обозначает, какой из операндов будет приемником. Если направление равно 0, то приемник - правый операнд, если же направление равно 1, то приемник - левый операнд. На примере инструкции mov bx,dx видно, как можно поменять местами операнды, изменив всего один бит:
Однако, давайте задумаемся, как поле направления будет вести себя, когда один из операндов непосредственное значение? Разумеется, что оно не может быть приемником и независимо от содержимого этого бита будет только источником. Инженеры Intel учли такую ситуацию и нашли оригинальное применение, часто экономящее в лучшем случае целых три байта. Рассмотрим ситуацию, когда операнду размером слово или двойное слово присваивается непосредственное значение по модулю меньшее 0100h. Ясно, что значащим является только младший байт, а стоящие слева нули по правилам математики можно отбросить. Но попробуйте объяснить это процессору! Потребуется пожертвовать хотя бы одним битом, чтобы указать ему на такую ситуацию. Вот для этого и используется бит направления. Рассмотрим следующую команду:
Если теперь флаг направления установить в единицу, то произойдет следующие:
Таким образом, мы экономим один байт в 16-разрядном режиме и целых три - в 32-разрядом. Этот факт следует учитывать при написании самомодифицирующегося кода. Большинство ассемблеров генерируют второй (оптимизированный) вариант, и длина команды оказывается меньше ожидаемой. На этом, кстати, основан очень любопытный прием против отладчиков. Посмотрите на следующий пример:
После выполнения инструкция в строке 0100h приобретет следующий вид:
То есть, текущая команда станет на байт короче! И "отрезанный" ноль теперь стал частью другой команды! Но при выполнении на "живом" процессоре такое не произойдет, т.к. следующие значение ip вычисляется еще до выполнения команды на стадии ее декодирования.
Совсем другое дело отладчики, и особенно отладчики-эмуляторы, которые часто вычисляют значение ip после выполнения команды (это легче запрограммировать). В результате чего наступает крах. Маленькая тонкость - до или после оказалась роковой, и вот вам в подтверждение дамп экрана:
Он изменит 6-ю строку на XOR SP,SP. Это "завесит" многие отладчики, и, кроме того, не позволит дизассемблерам отслеживать локальные переменные адресуемые через SP. Хотя IDA Pro и позволяет скорректировать стек вручную, для этого надо сначала понять, что SP обнулился. В приведенном примере это очевидно (но в глаза, кстати, не бросается), а если это произойдет в многопоточной системе? Тогда изменение кода очень трудно будет отследить, особенно в листинге дизассемблера. Однако, нужно помнить, что самомодифицирующийся код все же уходит в историю. Сегодня он встречается все реже и реже.
Кстати, IDA Pro вообще отказывается анализировать весь последующий код. Как это можно использовать? Да очень просто - если эмулировать еще два сегментных регистра в обработчике INT 06h, то очень трудно это будет как отлаживать, так и дизассемблировать программу. Однако, это опять-таки не работает под Win32!
Управляющие/отладочные регистры кодируются нижеследующим образом:
Заметим, что коды операций mov, манипулирующих этими регистрами, различны, поэтому-то и возникает кажущееся совпадение имен. С управляющими регистрами связана одна любопытная мелочь. Регистр CR1, как известно большинству, в настоящее время зарезервирован и не используется. Во всяком случае, так написано в русскоязычной документации. На самом деле регистр CR1 просто не существует! И любая попытка обращения к нему вызывает генерацию исключение INT 06h. Например, сир386 в режиме эмуляции процессора этого не учитывает и неверно исполняет программу. А все дизассемблеры, за исключением IDA Pro, неправильно дизассемблируют этот несуществующий регистр:
Все эти команды на самом деле не существуют и приводят к вызову прерывания INT 06h. He так очевидно, правда? И еще менее очевидно обращение к регистрам DR4-DR5. При обращении к ним исключения не генерируется.
Между прочим, IDA Pro 3.84 дезассемблирует не все регистры. Зато великолепно их ассемблирует (кстати, ассемблер этот был добавлен другим разработчиком).
Пользуясь случаем, акцентируем внимание на сложностях, которые подстерегают при написании собственного ассемблера (дизассемблера). Документация Intel местами все же недостаточно ясна (как в приведенном примере), и неаккуратность в обращении с ней приводит к ошибкам, которыми может воспользоваться разработчик защиты против хакеров.
Теперь перейдем к описанию режимов адресации микропроцессоров Intel. Тема очень интересная и познавательная не только для оптимизации кода, но и для борьбы с отладчиками.
Первым ключевым элементом является байт modR/M.
Рис. 3. Формат байта modR/M
Если mod содержит 11b, то два следующих поля будут представлять собой регистры. (Это так называемая регистровая адресация). Например:
Как отмечалось выше, по байту modR/M нельзя точно установить регистры. В зависимости от кода операции и префиксов размера операндов, результат может коренным образом меняться.
Биты 3-5 могут вместо определения регистра уточнять код операции (в случаи, если один из операндов представлен непосредственным значением). Младшие три бита всегда либо регистр, либо способ адресации, что зависит от значения mod. A вот биты 3-5 никак не зависят от выбранного режима адресации и задают всегда либо регистр, либо непосредственный операнд.
Формат поля R/M, строго говоря, не документирован, однако достаточно очевиден, что позволяет избежать утомительного запоминания совершенно нелогичной на первый взгляд таблицы адресаций (таблица 3).
Рис. 4. Формат поля R/M.
Возможно, кому-то эта схема покажется витиеватой и трудной для запоминания, но зубрить все режимы без малейшего понятия механизма их взаимодействия еще труднее, кроме того, нет никакого способа себя проверить и проконтролировать ошибки.
Действительно, в поле R/M все три бита тесно взаимосвязаны, в отличии от поля mod, которое задает длину следующего элемента в байтах.
Рис. 5. Формат команды в зависимости от поля mod.
Разумеется, не может быть смещения Offset 12, (т.к. процессор не оперирует с полуторными словами), а комбинация 11 указывает на регистровую адресацию.
Все вышесказанное проиллюстрировано в приведенной ниже таблице 3. Обратим внимание на любопытный момент - адресация типа [ВР] отсутствует. Ее ближайшим эквивалентом является [ВР+0]. Отсюда следует, что для экономии следует избегать непосредственного использования ВР в качестве индексного регистра. ВР может быть только базой. И mov ax,[bр] хотя и воспринимается любым ассемблером, но ассемблируется в mov ах,[bр+0], что на байт длиннее.
Исследовав приведенную ниже таблицу 3, можно прийти к выводу, что адресация в процессоре 8086 была достаточно неудобной. Сильнее всего сказывалось то ограничение, что в качестве индекса могли выступать только три регистра (ВХ, SI, DI), когда гораздо чаще требовалось использовать для этого СХ (например, в цикле) или AХ (как возвращаемое функцией значение).
Поэтому, начиная с процессора 80386 (для 32-разрядного режима), концепция адресаций была пересмотрена. Поле R/M стало всегда выражать регистр независимо от способа его использования, чем стало управлять поле mod, задающие, кроме регистровой, три вида адресации:
Видно, что поле mod по-прежнему выражает длину следующего поля - смещения, разве что с учетом 32-битного режима, где все слова расширяются до 32 бит.
Напомним, что с помощью префикса 0x67 можно и в 16-битном режиме использовать 32-битный режимы адресации, и наоборот. Однако, при этом мы сталкиваемся с интересным моментом - разрядность индексных регистров остается 32-битной и в 16-битном режиме!
В реальном режиме, где нет понятия границ сегментов, это действительно будет работать так, как выглядит, и мы сможем адресовать первые 4 мегабайта памяти (32 бита), что позволит преодолеть печально известное ограничение размера сегмента 8086 процессоров в 64К. Но такие приложения окажутся нежизнеспособными в защищенном или V86 режиме. Попытка вылезти за границу 64К сегмента вызовет исключение 0Dh, что приведет к автоматическому закрытию приложения, скажем, под управлением Windows. Аналогично поступают и отладчики (в том числе и многие эмуляторы, включая Cup386).
Сегодня актуальность этого приема, конечно, значительно снизилась, поскольку "голый DOS" практически уже не встречается, а режим его эмуляции Windows крайне неудобен для пользователей.
Вот она! Но что представляет собой код 0BAh? Попробуем определить это по трем младшим битам. Они принадлежат регистру DL(DX). А 0B4h 09h - это * AH,9. Теперь нетрудно догадаться, что оригинальный код выглядел как:
И это при том, что не требуется помнить код команды MOV! (Хотя это очень распространенная команда и запомнить ее код все же не помешает).
Вызов 21-го прерывания 0CDh 21h легко отыскать, если запомнить его символьное представление '=!' в правом окне дампа. Как нетрудно видеть, следующий вызов INT 21h лежит чуть правее по адресу 0Ch. При этом DX указывает на 0156h. Это соответствует смещению 056h в файле. Наверняка эта функция читает пароль. Что ж, уже теплее. Остается выяснить, кто и как к нему обращается. Ждать придется недолго.
При разборе байта 0Eh не забудьте, что адресации [ВР] не существует в природе. Вместо этого мы получим [offset16]. На размер регистра и приемник результата указывают два младших бита байта 08Ah. Они равны 10b. Следовательно, мы имеем дело с регистром CL, в который записывается содержимое ячейки [0156h].
Все, знакомые с ассемблером, усмотрят в этом действии загрузку длины пароля (первый байт строки) в счетчик. Неплохо для начала? Мы уже дизассемблировали часть файла и при этом нам не потребовалось знание ни одного кода операции, за исключением, быть может, 0CDh, соответствующего команде INT.
Вряд ли мы скажем, о чем говорит код 087h. (Впрочем, обращая внимание на его близость к операции NOP, являющейся псевдонимом XCHG AХ,AХ, можно догадаться, что 087h - это код операции XCHG). Обратим внимание на связанный с ним байт 0F2h:
Эта команда заносит в SI смещение пароля, содержащееся в DX. Такой вывод следует исключительно из смыслового значения регистров (код команды игнорируется). К сожалению, этого нельзя сказать о следующем байте - 0ACh. Это код операции LODSB, и его надо просто запомнить.
0x02 - код операции ADD, а следующий за ним байт - код AH,AL.
0хЕ2 - код операции LOOP, а следующий за ним байт - знаковое относительное смещение перехода.
Чтобы превратить его в знаковое целое, необходимо дополнить его до нуля, (операция NEG, которую большинство калькуляторов не поддерживают). Тот же самый результат мы получим, если отнимем от 0100h указанное значение (в том случае, если разговор идет о байте). В нашем примере это равно пяти. Отсчитаем пять байт влево от начала следующей команды. Если все сделать правильно, то вычисленный переход должен указывать на байт 0ACh (команда LODSB), впрочем, последнее было ясно и без вычислений, ибо других вариантов, по-видимому, не существует.
Почему? Да просто данная процедура подсчета контрольной суммы (или точнее хеш-суммы) очень типична. Впрочем, не стоит всегда полагаться на свою интуицию и "угадывать" код, хотя это все же сильно ускоряет анализ.
С другой стороны, хакер без интуиции - это не хакер. Давайте применим нашу интуицию, чтобы "вычислить", что представляет собой код следующей команды. Вспомним, что 0B4h (10110100b) - это MOV AН,imm8.
0BEh очень близко к этому значению, следовательно, это операция MOV. Осталось определить регистр-приемник. Рассмотрим обе команды в двоичном виде:
Как уже говорилось выше, младшие три бита - это код регистра. Однако, его невозможно однозначно определить без утончения размера операнда. Обратим внимание на третий (считая от нуля) бит. Он равен нулю для AН и единице в нашем случае. Рискнем предположить, что это и есть бит размера операнда, хотя этого явно и не уточняет Intel, но вытекает из самой архитектуры команд и устройства декодера микропроцессора.
Обратим внимание, что это, строго говоря, частный случай, и все могло оказаться иначе. Так, например, четвертый справа бит по аналогии должен быть флагом направления или знакового расширения, но увы - таковым в данном случае не является. Четыре левые бита - это код операции mov reg, imm. Запомнить его легко - это "13" в восьмеричном представлении.
Итак, 0BEh 0ЗВh 001h - это MOV SI, 013Bh. Скорее всего, 01ЗВh - это смещение, и за этой командой последует расшифровщик очередного фрагмента кода. А может быть и нет - это действительно смелое предположение. Однако, байты 0З0h 024h это подтверждают. Хакеры обычно так часто сталкиваются с функций хоr, что чисто механически запоминают значение ее кода.
Не трудно будет установить, что эта последовательность дизассемблируется как XOR [SI],AН. Следующий байт 046h уже нетрудно "угадать" - INC SI. Кстати, посмотрим, что же интересного в этом коде:
Третий бит равен нулю! Выходит команда должна выглядеть как INC AН! (Что кстати, выглядит непротиворечиво смысле дешифровщика). Однако, все же это INC SI. Почему мы решили, что третий бит - флаг размера? Ведь Intel этого никак не гарантировала! А команда 'INC byte' вообще выражается через дополнительный код, что на байт длиннее.Выходит, что как ни полезно знать архитектуру инструкций, все же таблицу кодов команд хотя бы местами надо просто выучить. Иначе можно впасть в глубокое заблуждение и совершить грубые ошибки. Хотя с другой стороны, знание архитектуры порой очень и очень помогает.
Аннотация: В этой лекции рассказывается об основных группах команд процессора, об особенностях выполнения различных команд, о методах организации подпрограмм.
Читайте также: