Сел детально разбираться с кодированием команд IA-32 во всех режимах. Само кодирование понятно, однако возник попутный вопрос: а какова разрядность регистров, участвующих в вычислении эффективного адреса в 64-разрядном режиме? Казалось бы, полный ответ на этот вопрос даёт первый том Интеловского руководства: Как явствует из этой цитаты, если префикс REX отсутствует или его бит W равен нулю (т.е. если используется 16- или 32-разрядный размер операндов), для вычисления адреса используется содержимое 32-разрядных регистров, и лишь при REX.W=1 применяются полные 64-разрядные регистры -- т.е. размер регистров, используемых для _адресации_, зависит от размера _операнда_. Однако это вызывает недоумение: 1) получается, что невозможно что-либо сделать со словом или двойным словом, находящимся в памяти, для обращения к которой необходимо использовать 64-разрядный регистр, поскольку для 64-разрядной адресации необходимо установить бит REX.W, а для обработки 16- или 32-разрядных операндов этот бит должен быть сброшен; 2) в 64-разрядном режиме разрядность данных по умолчанию равна 32 битам (именно поэтому для обработки 64-разрядных величин нужен префикс REX с установленным битом W), однако размер эффективного адреса по умолчанию равен 64 битам и меняется на 32 бита только с помощью префикса изменения размера адреса 67h (об этом прямо говорится в документации AMD; документация Intel обходит этот вопрос молчанием): 3) наконец, ни в документации AMD, ни в документации Intel не упоминается о том, что префикс REX используется для изменения размера регистров, используемых для вычисления адреса, там говорится исключительно о размере операнда. В общем, всё это наводит на мысль, что приведённая в начале поста цитата из интеловского мануала содержит ошибку, и на самом деле для вычисления эффективного адреса по умолчанию используются 64-разрядные регистры независимо от размера операнда, и лишь при наличии префикса 67h используются 32-разрядные регистры. Но хотелось бы узнать, что по этому поводу думают другие и проверял ли кто это на практике?
Именно так. REX используется только для доступа к новым регистрам (р8-р15), т.е. используются только REX.B/R/X. W влияет исключительно на размер операнда. С самим же размером операнда есть нюанс в случае команд, неявно использующих xSP и near-branch (push/pop, retn, call/jmp near и пр.) - там по умолчанию размер операнда 64 бита (рекс не нужен), 66h переключает в 16 бит, 32 бита не кодируются. зы В интеловских манах такие принципиальные (!) ошибки вообще можно мешками грузить.
Vic3Dexe Спасибо. Я тоже был уверен, что это ошибка, но... Полума хорошо, а полтора -- лучше, как известно А что качество ещё то, это я в курсе. И если откровенных ошибок всё ж не слишком много, то просто неясностей и т.п. -- пруд пруди. Кстати говоря, изложил свои изыскания по поводу кодирования команд вот здесь: http://ru.osdev.wikia.com/wiki/Кодирование_команд. Если вдруг кого туда занесёт и он обнаружит неточности, ошибки и т.д., буду благодарен за замечания и исправления
SII А можешь рассказать как кодируют команды сопроцессора и SSE а то не очень понятно как они кодируются. Какиет-о там последние числа из байта опкода отводятся под них. Особенно в том плане что там в опкод может входить байт 66h и 67h - и это будет не префикс, а именно опкод.
Pavia 67h в SSE командах не используется. Там используются 66, F2, F3. Отличие их от обычных префиксов в том, что в некоторых SSE командах одновременное присутствие 66 и Fx недопустимо и вызывает исключение, а вот из F2/F3 действует последний как в строковых командах. А процессоры без SSE-расширений, у которых есть только MMX, префиксы Fx игнорировали. А код команды в последнем байте это чаще всего встречается в 3DNow, хотя может там сейчас и другие команды поселились.
По навскидку: 1. AMD выделяет F0 в отдельную группу, и считает, что групп 5, а не 4. Реально, их все равно 4, т.к. ни одна команда, принимающая lock, не принимает rep/repne и наоборот, так что их можно считать взаимоисключающими (что, видимо, и сделала интел). 2. lock в тех случаях, где его юзать нельзя приводит к #UD, а не игнорируется. 3. "отклонение" я бы переименовал в более привычное "смещение", а то воспринимается как отклонение от нормы =) 4. Ну и общая грамматика, местами глюки типа
2Pavia В SSE черт ногу сломит. Закономерностей очень мало, костыль - он костыль и есть. addps 0F 58 addpd 66 0F 58 addss F3 0F 58 addsd F2 0F 58 Вот тут 66/F2/F3 - именно часть опкода, ни о каких 16 битах или rep/repne речи нет. REX, если он есть должен стоять перед 0F, т.е. addss F3 4x 0F 58 хотя F3 по-прежнему считается опкодом
Pavia 66h там будет всё равно префиксом, но префиксом обязательным, т.е. де-факто это часть кода операции (поскольку без префикса получается совсем другая операция или вообще никакая не получается), хотя де-юре префикс (а посему может быть отделён от собственно кода операции префиксом REX). Специально же никакими группами команд я пока не занимался, ограничился лишь общим форматом кодирования, поэтому никаких "интимных" подробностей про SSE не сообщу. Когда дойдут руки, займусь в т.ч. и ими, но это нескоро будет, поскольку практической нужды пока нет (а вот в общем кодировании разобраться было нужно). Vic3Dexe 1. Я писал по интелу, потому и групп 4. В принципе, можно будет отметить, что АМД выделяет 5 групп. 2. Вроде интел об этом не упоминает, надо дополнить будет. 3. "Отклонение" я использовал, поскольку смещение -- это offset, т.е. вторая часть логического адреса (первая -- селектор сегмента), ну а displacement не является частью логического адреса, а используется для вычисления смещения. Использовать одинаковое слово не хотелось бы из-за возможности смешивания, а ничего более удачного я не придумал. 4. Угу, глаз замыливается, уже не видишь очевидных ляпов. Отдохну и попробую вычитать-исправить.
Black_mirror Именно вызывает исключение? Насколько я понимаю, x86 вообще лояльно отностися к невлияющим на инструкцию префиксам, просто игнорируя их (за исключением специально оговоренных случаев).
Vic3Dexe Посмотрел доку: там говорится, что использование LOCK с непредусмотренными для него инструкциями (или с предусмотренными, но приёмником которых является не память) _может_ привести к #UD -- но не _обязано_ приводить. Отметил это обстоятельство на Вике.
В осдевовской вике, хтати, коррективы всякие внёс. Но, если кто будет мои бредни там читать, по-прежнему просьба сообщать о всех ляпах, что найдутся medstrax1 Коды репов (F2 и F3) в ряде команд же используются как обязательные префиксы (де-факто части кода операции), так что, может, из-за этого крышу и рвёт...
Хз из за чего рвет. Emms ничем не выделяется среди других, однако на rep'e сносит башню только ей. На прочих командах не увидел влияния репа
Тож перечитал. Там еще тоньше: 1. С любой левой инструкцией - #UD однозначно (и в тексте, и в списке эксепшенов, у АМД тож самое). 2. С правильной инструкцией, но с операндом-приемником НЕ памятью - может привести (у АМД не нашел об этом). А вообще, конечно, не суть важны такие тонкости. Все равно в lock с операндом не-памятью смысла ноль.