Intel x86 Assembly Language & Microarchitecture
Основы регистрации
Поиск…
16-разрядные регистры
Когда Intel определила оригинальный 8086, это был 16-разрядный процессор с 20-битной адресной шиной (см. Ниже). Они определили 8 16-разрядных регистров общего назначения, но дали определенные роли для определенных инструкций:
-
AXАккумуляторный регистр.
Многие коды операций либо принимали этот регистр, либо быстрее, если он был указан. -
DXРегистр данных.
Иногда это сочеталось как 16 бит 32-битного значения сAX- например, в результате умножения. -
CXРегистр счетчика.
Это использовалось в ряде ориентированных на цикл инструкций в качестве неявного счетчика для этих циклов - например,LOOPNE(цикл, если не равный) иREP(повторное перемещение / сравнение) -
BXБазовый регистр.
Это можно использовать для индексации базы структуры в памяти - ни один из вышеперечисленных регистров не может использоваться для непосредственного индексации в память. -
SIРегистр исходного индекса.
Это был неявный исходный индекс в памяти для определенных операций перемещения и сравнения. -
DIРегистр индекса назначения.
Это был неявный индекс назначения в память для определенных операций перемещения и сравнения. -
SPРегистр указателя стека.
Это наименее универсальный регистр в наборе! Он указал на текущую позицию в стеке, которая была явно использована для операцийPUSHиPOP, неявно дляCALLиRETс подпрограммами, и ОЧЕНЬ неявно во время прерываний. Таким образом, использование его для чего-либо другого было опасным для вашей программы! -
BPРегистр базового указателя.
Когда подпрограммы вызывают другие подпрограммы, стек содержит несколько «кадров стека».BPможет использоваться для хранения текущего кадра стека, а затем, когда была вызвана новая подпрограмма, она может быть сохранена в стеке, новый стек стека создан и используется, а при возврате из внутренней подпрограммы можно восстановить значение старого фрейма стека ,
Заметки:
Первые три регистра не могут использоваться для индексирования в память.
BX,SIиDIпо умолчанию в текущий сегмент данных (см. Ниже).MOV AX, [BX+5] ; Point into Data Segment MOV AX, ES:[DI+5] ; Override into Extra SegmentDI, при использовании в операциях памяти в оперативную память , такие какMOVSиCMPS, исключительно использует Добавочный сегмент (см ниже). Это нельзя переопределить.SPиBPиспользуют сегмент стека (см. Ниже) по умолчанию.
32-разрядные регистры
Когда Intel выпустила 80386, они обновились с 16-разрядного процессора до 32-разрядного. 32-битная обработка означает две вещи: обе манипулируемые данные были 32-битными, а адреса доступа к памяти были 32-разрядными. Чтобы сделать это, но по-прежнему совместимы с более ранними процессорами, они представили совершенно новые режимы для процессора. Это было либо в 16-битном режиме, либо в 32-битном режиме, но вы можете переопределить этот режим по принципу «инструкция за инструкцией» для данных, адресации или обоих!
Прежде всего, они должны были определить 32-битные регистры. Они сделали это, просто расширив существующие восемь из 16 бит до 32 бит и предоставив им «расширенные» имена с префиксом E : EAX , EBX , ECX , EDX , ESI , EDI , EBP и ESP . Нижние 16 бит этих регистров были такими же, как и раньше, но верхние половины регистров были доступны для 32-битных операций, таких как ADD и CMP . Верхние половинки не были доступны отдельно, как это было сделано с 8-битными регистрами.
Процессор должен был иметь отдельные 16-битные и 32-битные режимы, потому что Intel использовал одни и те же коды операций для многих операций: CMP AX,DX в 16-битном режиме и CMP EAX,EDX в 32-битном режиме имели точно такие же коды операций ! Это означало, что один и тот же код НЕ МОЖЕТ запускаться в любом режиме:
Код операции для «Переместить немедленно в
AX» равен0xB8, за которым следуют два байта0xB8 0x12 0x34значения:0xB8 0x12 0x34
Код операции для «Переместить немедленно в
EAX» равен0xB8, за которым следуют четыре байта непосредственного значения:0xB8 0x12 0x34 0x56 0x78
Таким образом, ассемблер должен знать, в каком режиме находится процессор при выполнении кода, чтобы он знал, что испускает правильное количество байтов.
8-битные регистры
Первые четыре 16-битных регистра могли бы иметь их верхнюю и нижнюю половину байт, которые были доступны непосредственно в качестве собственных регистров:
-
AHиAL- это верхние и нижние половинки регистраAX. -
BHиBL- это верхние и нижние половинки регистраBX. -
CHиCL- это верхние и нижние половинки регистраCX. -
DHиDL- это верхние иDXрегистры регистраDX.
Обратите внимание, что это означает, что изменение AH или AL немедленно изменит AX ! Также обратите внимание, что любая операция в 8-битном регистре не может повлиять на его «партнер» - приращение AL такое, что оно переполнено с 0xFF на 0x00 , не изменит AH .
64-разрядные регистры также имеют 8-битные версии, представляющие их нижние байты:
-
SILдляRSI -
DILдляRDI -
BPLдляRBP -
SPLдляRSP
То же самое относится к регистрам R8 через R15 : их соответствующие нижние части байта называются R8B - R15B .
Сегментные регистры
сегментация
Когда Intel разрабатывала оригинальные 8086, уже было множество 8-разрядных процессоров с 16-разрядными возможностями, но они хотели создать настоящий 16-разрядный процессор. Они также хотели создать что-то лучшее и более способное, чем то, что уже было там, поэтому они хотели иметь доступ к максимальному объему памяти 65 536 байт, что подразумевалось 16-разрядными регистрами адресации.
Регистры исходного сегмента
Поэтому они внедрили идею «Сегменты» - блок памяти объемом 64 килобайта, индексированный 16-разрядными регистрами адресов, которые могут быть повторно основаны на адресах различных областей общей памяти. Чтобы удерживать эти сегментные базы, они включали регистры сегментов:
-
CSРегистр сегмента кода.
Это содержит сегмент кода, который в настоящее время выполняется, индексируется неявным регистромIP(указателем инструкций). -
DSРегистр сегмента данных.
Это содержит сегмент по умолчанию для данных, которыми управляет программа. -
ESРегистр дополнительных сегментов.
Это содержит второй сегмент данных для одновременных операций с данными по всей памяти. -
SSРегистр сегмента стека.
Это удерживает сегмент памяти, который содержит текущий стек.
Размер сегмента?
Регистры сегментов могут быть любого размера, но их ширина 16 бит облегчает взаимодействие с другими регистрами. Следующий вопрос: должны ли сегменты перекрываться, и если да, то сколько? Ответ на этот вопрос будет определять общий объем памяти, к которому можно получить доступ.
Если бы не было перекрытия вообще, тогда адресное пространство было бы 32 бита - 4 гигабайта - совершенно неслыханный размер в то время! Более «естественное» перекрытие 8 бит создаст 24-битное адресное пространство или 16 мегабайт. В конце концов Intel решила сохранить еще четыре адресных контакта на процессоре, сделав адресное пространство 1 мегабайт с 12-битным перекрытием - они считали это достаточно большим на время!
Больше регистров сегментов!
Когда Intel разрабатывала 80386, они признали, что существующего набора из 4 регистров сегмента недостаточно для сложности программ, которые они хотели, чтобы они могли поддерживать. Поэтому они добавили еще два:
-
FSРегистр дальнего сегмента -
GSРегистр глобального сегмента
Эти новые регистры сегментов не имели применений, которые использовались в процессорах: они были просто доступны для любого программиста.
Некоторые говорят, что имена были выбраны просто для продолжения темы
C,D,Eсуществующего набора ...
64-разрядные регистры
AMD - производитель процессоров, который лицензировал дизайн 80386 от Intel для производства совместимых, но конкурирующих версий. Они внесли внутренние изменения в дизайн, чтобы улучшить пропускную способность или другие улучшения в дизайне, при этом все еще можно выполнять одни и те же программы.
Для однонаправленного Intel они придумали 64-битные расширения для 32-битного дизайна Intel и выпустили первый 64-битный чип, который все еще мог бы работать с 32-разрядным кодом x86. Intel закончила дизайн AMD в своих версиях 64-битной архитектуры.
64-битная конструкция сделала ряд изменений в наборе регистров, сохраняя обратную совместимость:
- Существующие регистры общего назначения были расширены до 64 бит и названы с префиксом
R:RAX,RBX,RCX,RDX,RSI,RDI,RBPиRSP.Опять же, нижние половины этих регистров были теми же
Eprefix-регистрами, что и раньше, и верхние половины не могли быть независимо доступны. - Добавлено еще 8 64-битных регистров и не названы, а просто пронумерованы:
R8,R9,R10,R11,R12,R13,R14иR15.- 32-разрядная
R8Dполовина этих регистров составляет отR8DдоR15D(D для DWORD, как обычно). - К младшим 16 битам этих регистров можно было получить суффикс
Wдо имени регистра:R8WR15W.
- 32-разрядная
- Теперь можно получить доступ к самым низким 8 битам из всех 16 регистров:
- Традиционные
AL,BL,CLиDL; - Низкие байты (традиционно) указателя регистрируются:
SIL,DIL,BPLиSPL; - А низкие байты 8 новых регистров:
R8BчерезR15B. - Однако
AH,BH,CHиDHнедоступны в инструкциях, в которых используется префикс REX (для 64-разрядного размера операнда или для доступа к R8-R15 или для доступа кSIL,DIL,BPLилиSPL). С префиксом REX бит-шаблон машинного кода, который раньше означалAHозначаетSPLи т. Д. См. Таблицу 3-1 руководства по эксплуатации инструкции Intel (том 2).
- Традиционные
Запись в 32-разрядный регистр всегда нулирует верхние 32 бита регистра полной ширины, в отличие от записи в 8 или 16-разрядный регистр (который сливается со старым значением, что является дополнительной зависимостью для исполнения вне порядка ).
Регистрация флагов
Когда арифметический логический блок x86 (ALU) выполняет операции типа NOT и ADD , он помещает результаты этих операций («стал нулевым», «переполнен», «стал отрицательным») в специальном 16-битном регистре FLAGS . 32-разрядные процессоры обновили это до 32 бит и назвали его EFLAGS , в то время как 64-разрядные процессоры обновили его до 64 бит и назвали его RFLAGS .
Коды условий
Но независимо от имени, регистр не доступен напрямую (за исключением нескольких инструкций - см. Ниже). Вместо этого отдельные флаги ссылаются в определенных инструкциях, таких как условный переход или условный набор, известный как Jcc и SETcc где cc означает «код условия» и ссылается на следующую таблицу:
| Код условия | название | Определение |
|---|---|---|
E , Z | Равно, ноль | ZF == 1 |
NE , NZ | Не равно, но не ноль | ZF == 0 |
O | перелив | OF == 1 |
NO | Нет переполнения | OF == 0 |
S | подписанный | SF == 1 |
NS | Не подписано | SF == 0 |
P | паритет | PF == 1 |
NP | Нет четности | PF == 0 |
| -------------- | ---- | ---------- |
C , B , NAE | Нести, ниже, не выше или равно | CF == 1 |
NC , NB , AE | Не переносите, не ниже, выше или равно | CF == 0 |
A , NBE | Выше, не ниже или равно | CF == 0 и ZF == 0 |
NA , BE | Не выше, ниже или равно | CF == 1 или ZF == 1 |
| --------------- | ---- | ---------- |
GE , NL | Больше или равно, не меньше | SF == OF |
NGE , L | Не больше или равно, меньше | SF ! = OF |
G , NLE | Больше, не меньше или равно | ZF == 0 и SF == OF |
NG , LE | Не больше, меньше или равно | ZF == 1 или SF ! = OF |
В 16 бит вычитание 1 из 0 равно либо 65,535 либо -1 зависимости от того, используется ли беззнаковая или подписанная арифметика, но пункт назначения имеет значение 0xFFFF любом случае. Только интерпретируя коды условий, значение ясно. Еще более важно, если 1 вычитается из 0x8000 : в беззнаковой арифметике, которая просто меняет 32,768 на 32,767 ; в то время как в знаковой арифметике он меняет -32,768 в 32,767 - гораздо более примечательное переполнение!
Коды условий сгруппированы в три блока в таблице: знак-нерелевантный, неподписанный и подписанный. Именование внутри последних двух блоков использует «Выше» и «Ниже» для неподписанных, а «Большой» или «Меньше» для подписания. Таким образом, JB будет «Jump if Below» (без знака), тогда как JL будет «Jump if Less» (подписано).
Доступ к FLAGS напрямую
Вышеупомянутые коды условий полезны для интерпретации предопределенных понятий, но фактические биты флага также доступны непосредственно со следующими двумя инструкциями:
-
LAHFЗагрузить регистрAHс помощью флагов -
SAHFStoreAHрегистрируется во флагах
С этими инструкциями копируются только определенные флаги. Весь регистр FLAGS / EFLAGS / RFLAGS можно сохранить или восстановить в стеке:
-
PUSHF/POPFPush / pop 16-битFLAGSв / из стека -
PUSHFD/POPFDPush / pop 32-разрядныеEFLAGSв / из стека -
PUSHFQ/POPFQPush / pop 64-разрядныеRFLAGSна / из стека
Обратите внимание, что прерывания сохраняют и восстанавливают текущий регистр [R/E]FLAGS автоматически.
Другие флаги
Помимо флагов ALU, описанных выше, регистр FLAGS определяет другие флагов системного состояния:
-
IFФлаг прерывания.
Это задано с инструкциейSTIчтобы глобально разрешить прерывания и очиститься с помощью командыCLIчтобы глобально отключить прерывания. -
DFФлаг направления.
Память-память операции , такие какCMPSиMOVS(для сравнения и перемещения между ячейками памяти) автоматически увеличивать или уменьшать регистры индекса в качестве части инструкции. ФлагDFопределяет, что происходит: если он очищен командойCLD, они увеличиваются; если они установлены с инструкциейSTD, они уменьшаются. -
TFФлаг ловушки. Это флаг отладки. Установка этого параметра превратит процессор в режим «одноступенчатый»: после выполнения каждой команды он вызывается «Обработчик прерываний с одним шагом», который, как ожидается, будет обрабатываться отладчиком. Нет инструкций по установке или очистке этого флага: вам нужно манипулировать битом, пока он находится в памяти.
Флаги 80286
Чтобы поддерживать новые многозадачные объекты в 80286, Intel добавила дополнительные флаги в регистр FLAGS :
-
IOPLУровень привилегий ввода-вывода.
Для защиты многозадачного кода некоторые задачи требовали привилегий для доступа к портам ввода-вывода, в то время как другим пришлось прекратить доступ к ним. Intel представила четырехуровневую шкалу Privilege, причем002 является наиболее привилегированным, а112 - наименее. ЕслиIOPLменьше текущего уровня привилегий, любая попытка доступа к портам ввода-вывода, а также включение или отключение прерываний приведет к сбою общей защиты. -
NTВложенная задача.
Этот флаг был установлен, если одна задачаCALLизменила другую задачу, которая вызвала контекстный переключатель. Флаг установки сказал процессору, чтобы сделать контекстный переключатель обратно, когдаRETбыл выполнен.
80386 Флаги
«386 нужны дополнительные флаги для поддержки дополнительных функций, разработанных в процессоре.
-
RFФлаг возобновления.
В «386» добавлены регистры Debug, которые могут вызывать отладчик при различных аппаратных доступах, таких как чтение, запись или выполнение определенного местоположения memry. Однако, когда обработчик отладки вернулся для выполнения команды, доступ немедленно повторно вызовет обработчик отладки! Или, по крайней мере, это было бы, если бы не флаг Возобновления, который автоматически устанавливается при входе в обработчик отладки и автоматически очищается после каждой инструкции. Если флажок Resume установлен, обработчик Debug не вызывается. -
VMВиртуальный 8086 Флаг.
Для поддержки старого 16-битного кода, а также более нового 32-разрядного кода 80386 может запускать 16-разрядные задачи в режиме «Виртуальный 8086» с помощью администратора Virtual 8086. ФлагVMуказывал, что эта задача представляет собой задачу Virtual 8086.
80486 Флаги
По мере совершенствования архитектуры Intel она стала быстрее благодаря такой технологии, как кэширование и суперскалярное выполнение. Это должно было оптимизировать доступ к системе, сделав предположения. Чтобы контролировать эти предположения, требуется больше флагов:
- Флаг проверки соответствия
ACАрхитектура x86 всегда может обращаться к значениям многобайтовой памяти на любой границе байта, в отличие от некоторых архитектур, которые требовали, чтобы они были выровнены по размеру (4-байтовые значения должны были быть на 4-байтных границах). Тем не менее, это было менее эффективно, так как для доступа к неуравновешенным данным необходимы множественные обращения к памяти. Если был установлен флагAC, то неприглаженный доступ приведет к возникновению исключения, а не к выполнению кода. Таким образом, код может быть улучшен во время разработки с помощью набораAC, но отключен для производственного кода.
Флаги Pentium
Pentium добавил больше поддержки для виртуализации, а также поддержку инструкции CPUID :
-
VIFВиртуальный флаг прерывания.
Это виртуальная копияIFэтой задачи - должна ли эта задача отключать прерывания, фактически не влияя на глобальные прерывания. -
VIPОжидающий флаг виртуального прерывания.
Это указывает на то, что прерывание было фактически заблокированоVIF, поэтому, когда Task выполняетSTIдля него может быть поднято виртуальное прерывание. -
ID. Идентификационный флагCPUID.
Разрешить или не разрешать этой Задаче выполнять инструкциюCPUID. Виртуальный монитор может запретить его и «лгать» запрашивающей Задаче, если он выполняет инструкцию.