Принципы кодирования инструкций Intel x86(-64) или "ехал префикс через префикс"

Тема в разделе "WASM.ARTICLES", создана пользователем aa_dav, 6 окт 2021.

  1. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    457
    Введение

    С давних пор меня интересовало то как процессоры Intel x86 кодируют свои инструкции.
    Будучи в детстве владельцем клона ZX Spectrum я уже тогда сталкивался с таблицами кодов инструкций его процессора Z80, как например тут: clrhome.org/table/
    В таком виде очень хорошо просматривается принцип кодирования этих инструкций — наглядно видно как они упорядочены и по каким битам раскиданы.
    Но вот для x86 таких таблиц как то не удавалось найти, а то как эти коды пояснялись в руководствах от самого Intel было несистематизировано и поэтому не воспринималось.
    Однако пару месяцев назад я наконец то наткнулся на табличный вид однобайтовых инструкций от i8086 до i386, поразглядывал его и проникся тем что тут и как кодируется.
    Более того — в процессе этого обзорного ознакомления я проникся еще тем как эволюционировала система команд x86 с поколениями процессоров и решил вкратце эти вехи законспектировать тут. Это ни в коем случае не полное справочное руководство, но скорее обзорное знакомство вместе с историческим экскурсом которое возможно поможет кому то быстро понять основные принципы кодирования инструкций x86 перед более углубленным изучением по таблицам.

    Почти вся важная информация взята из следующих ссылок:
    Ссылка на табличную (и неполную) форму: sparksandflames.com/files/x86InstructionChart.html
    Полное описание инструкций 32–битного x86: ref.x86asm.net/coder32.html
    Полное описание инструкций 64–битного x86–64: ref.x86asm.net/coder64.html

    Intel 8086

    Откроем табличную форму (первую ссылку). В ней описаны все возможные однобайтовые коды инструкций x86 начиная от 16–битного 8086 вплоть до 32–битного 80386.
    Все коды мы будем рассматривать в 16–ричной форме — так удобнее. Перед такими числами я буду ставить знак доллара, например $1F.
    По сути таблица получается упорядочена в строки которые соответствуют первой 16–ричной цифре байта и в колонки которые соответствуют его второй цифре. Т.е. код инструкции $XY соответствует пересечению строки X и столбца Y.

    Если взять 8–битные процессоры типа MOS 6502 или i8080, то их системы команд стараются уложить код инструкции процессора в один байт. Это как раз такая таблица и всего 256 возможных вариантов инструкций.
    Из–за этого этим системам команд присуща довлеющая асимметричность. Как правило есть главный регистр именуемый аккумулятором и он неявно предполагается первым операндом в большинстве команд которым операнды нужны.
    Но у такого подхода есть много недостатков — иллюзорная краткость кода инструкции ограниченная одним байтом на практике заставляет прописывать много инструкций перекладывающих значения и результаты из аккумулятора в память и другие регистры.
    Поэтому экономия на байтах получается довольно злобно–буратинистая и выходит боком.
    Другой пример — 16–битная архитектура PDP–11 (в СССР известная в ПК серии БК–0010) которая полюбилась программистам как раз за высококачественную симметричность и равноправность регистров общего назначения во всех операциях. Но шло это за ту цену, что коды инструкций всегда были 16–битные, т.е. двухбайтовые.

    Когда Intel разрабатывали 16–битный процессор 8086 они совместили оба подхода: по возможности коды инструкций были однобайтовыми, но там где надо они становились двухбайтовыми.
    Вернёмся к таблице — в ней светло–зеленым цветом обозначены однобайтовые инструкции.
    Например ряд $40–4F весь заполнен инструкциями «INC reg» и «DEC reg», где reg — это один из восьми 16–битных регистров общего назначения архитектуры i8086: AX, BX, CX, DX, SP, BP, SI и DI.
    Эти инструкции увеличивают или уменьшают соответствующий регистр на единицу — сама инструкция при этом занимает всего один байт и за ней сразу же идёт следующая инструкция.
    Многие инструкции после опкода содержат некоторые данные. Так, например, ряд $70–7F содержит 16 вариантов инструкций условного перехода которые после кода содержат байт смещения на которое надо перейти (относительно текущей инструкции) при срабатывании условия. Таким образом при однобайтовом опкоде сама инструкция занимает два байта.

    Но вот что если нам надо выполнить двухоперандную инструкцию и при этом иметь высокую ортогональность? i8086 в таких случаях расширяет опкод инструкции до двух байт и второй байт называется «modR/M».
    Рассмотрим это на примере первых четырёх опкодов $00–03 — инструкции «ADD X, Y», т.е. сложения X и Y с занесением результата в X. Такие опкоды окрашены в таблице тёмно–зеленым — это значит, что сразу после основного опкода идёт байт modR/M сообщая какие у инструкции аргументы.
    Во первых опкоды $00 и $02 работают с байтами (суффиксы «b» у аргументов в таблице), а опкоды $01 и $03 — со словами (суффиксы «v»). Это первое что решает главный опкод — с байтом или словом мы работаем. В i8086 слово всегда величиная 16–битная (или двухбайтовая).
    Во вторых опкоды $00 и $01 работают с порядком «E, G», а $02 и $03 с порядком «G, E». Это второе что выбирает основной опкод — порядок аргументов.
    В чём тут дело: все двухоперандные инструкции i8086 имеют одним из аргументов регистр общего назначения (РОН), а вторым — РОН или ячейку памяти. Т.е. ячейка памяти в аргументах может встречаться только один раз. В таблице аргумент обозначенный литерой «E» это как раз возможная ячейка памяти. Поэтому где она возможно будет употребляться — первым или вторым аргументом — важно. Если оба аргумента регистры — это уже не имеет значения и просто получается две разных возможности закодировать одну и ту же инструкцию.
    Итак, один из четырёх опкодов «ADD» сообщает нам с байтом или словом мы работаем и первым или вторым аргументом стоит возможно ячейка памяти. Следом за ним идёт байт «modR/M».
    Этот байт имеет следующее битовое представление:
    GGG это всегда тут код регистра общего назначения, восемь вариантов по возрастанию: AX, CX, BX, DX, SP, BP, SI, DI.
    Заметьте, что CX и BX как будто бы перепутаны местами — мнемонические обозначения не совпали с физическими номерами, ведь CX подразумевает «Counter/Счётчик», а BX «Base/База».
    Если mm=0, то и EEE — это тоже номер РОН.
    Но если mm=1,2 или 3, то EEE кодирует адрес ячейки памяти, всех возможных комбинаций следующей формулы (квадратные скобки здесь служат обозначением косвенного обращения, а круглые скобки окружают опциональные варианты):
    Т.е. или BX или BP в сумме с или SI или DI плюс или 16–битное или 8–битное смещение. При этом любые из слагаемых могут отсутствовать, но хотя бы один регистр должен быть.
    Однако из этой схемы выбивается случай который должен был бы кодироваться как [ BP ] — эта комбинация запрещена и вместо неё срабатывает [ addr16 ], т.е. непосредственный адрес ячейки памяти в инструкции который следует за байтом modR/M.
    Это весьма разумно, т.к. такой режим адресации нужен, а режим адресации [ BP ] во первых можно закодировать как [ BP + 0 ], но и это как правило не требуется, т.к. по [ BP + 0 ] лежит и не локальная переменная и не аргумент функции, а адрес возврата из неё — и адресовать его через BP такой конструкцией просто не нужно (BP по дизайну архитектуры предназначен для ссылок на локальные переменные и аргументы функции). Логично и элегантно.
    Все эти варианты можно посмотреть по ссылке под заголовком «MOD R/M 16».

    Так кодируется большое количество инструкций в i8086, но есть еще один вариант использования байта modR/M — в инструкциях однооперандных.
    Что, если нам нужно, например, инкрементировать или декрементировать ячейку памяти?
    Тут получается, что поле GGG из modR/M как бы и не нужно. И инженеры Intel воспользовались этим — и во многих однооперандных инструкциях зачастую код инструкции переползает в поле GGG байта modR/M.
    В таблице такие группы инструкций обозначены как #2, #3, #4 и #5.
    Здесь нам уже понадобится открыть ссылку ref.x86asm.net/coder32.html и прокрутить до опкода $FE или открыть эту ссылку: ref.x86asm.net/coder32.html#xFE
    У этого опкода появляются числа в колонке «o». Это значит, что в поле «GGG» ожидается это число и тогда инструкция поведёт себя или как INC или как DEC.
    Таким образом опкод $FE это инкремент/декремент байта с байтом modR/M, а что именно — инкремент или декремент решает поле «GGG» в байте modR/M.
    А опкод $FF — это инкремент/декремент слова с байтом modR/M. Но не только! Если присмотреться к этой таблице, то видно что в опкод $FF еще прописали некоторые варианты инструкций CALL, JMP и PUSH (насколько я понял, это сделали не в i8086, а позднее).
    Таким же образом сделаны инструкции побитовой прокрутки (группа #2) и умножения/деления (группа #3) которые предполагают первым аргументом аккумулятор. И некоторые другие.

    Тут же еще стоит упомянуть, что в i8086 инструкции могли предваряться однобайтовыми префиксами — это была группа префиксов переопределения сегментных регистров (CS:, DS:, ES: и SS:), префиксы автоматического повторения строковых инструкций (REP и REPE) и ESC-префиксы для работы с сопроцессором.

    Intel 80286

    Этот процессор ввёл защищенный режим при полном сохранении 16–битности и сейчас, имхо, немного программистов вообще знают что за причудливый это был зверь изнутри (под ним работали Windows 1.x-3.x).
    Но нам тут интересен главным образом вот какой момент: для управления защищённым режимом «двойке» потребовалось ввести с пару десятков административных инструкций.
    И помещать их в основной пул однобайтовых инструкций было уже по существу некуда.
    И вот тут обратите внимание на опкод $0F из первой таблицы — он там помечен как «TWO BYTE».

    Если присмотреться к логике опкодов здесь должна была бы находиться инструкция «POP CS» — извлечение из стека регистра сегмента данных. С точки зрения практики такая инструкция не имела пользы — она прыгнула бы в иной сегмент по текущему смещению IP, что есть бред. Поэтому эту инструкцию в i8086 запретили использовать. Так вот в 80286 её использовали как префикс двухбайтовой инструкции. Административные инструкции стали записываться как $0F XX и с тех пор префикс $0F — стал весьма коренной вещью в системе команд x86.

    Ниже мы к нему еще много раз вернёмся…

    Intel 80386

    Эпоха 32 бит сильно изменила ландшафт архитектуры x86. Во первых — регистры были расширены до 32 бит, а главное — они стали по настоящему равноправны. Какой ценой?
    В режиме 32 бит как правило ОС настраивала процессор так, что по умолчанию то что было 16–битным словом в i8086 здесь трактовалось как 32–битное двойное слово.
    Т.е. все операнды в таблице обозначаемые через суффикс «v» по умолчанию в 32–битных ОС стали 32–битными.
    Регистры превращались соответственно в 32–битные eAX, eBX, eCX, eDX, eSP, eBP, eSI, eDI.
    Чтобы переключить команду в режим одинарного 16–битного слова был заведён байтовый префикс $66 (OPSIZE). Он вставляется перед инструкцией и временно меняет ей битность слова с 32 бит на 16 или обратно (смотря какой сейчас режим выставлен по умолчанию).
    Есть аналогичный префикс $67 (ADSIZE) для разрядности адресов, но если я правильно понял он широко не использовался в отличие от $66.

    Что же касается неравноправности регистров — она проявлялась в адресациях ячеек памяти — только ограниченный круг регистров и в ограниченных комбинациях мог участвовать в косвенных адресациях в i8086.
    В i386 эти ограничения были сняты за счёт модификации смысла байта modR/M.
    Во что оно превратилось можно посмотреть там же по первой ссылке под заголовком «MOD R/M 32».
    Главное отличие: большинство комбинаций аргумента EEE в modR/M теперь описывается как
    Т.е. комбинация из любого РОН плюс смещения из байта или слова, где смещение может отсутствовать.
    Точно так же как и в i8086 комбинация [ eBP ] изменена на непосредственный адрес: [ addr ].
    Но главное, что все косвенные адресации с регистром eSP изменены на адресацию с использованием байта SIB.
    Это значит, что если в байте modR/M выбран такой режим, то сразу за ним следует еще один новый байт SIB который еще сильнее уточняет режим адресации!
    Байт SIB (Scale–Index–Base) кодируется битово как
    где III и BBB — коды РОН индекса и базы, а SS кодирует множитель перед индексом — четыре варианта: 1, 2, 4, 8.
    Таким образом вместе с байтом SIB в i386 самый сложный режим адресации выглядит как:
    [ (1|2|4|8 * Index) + (Base) + (Offs8|Offs32) ]

    Т.е. любой РОН помноженный на константу 1,2,4,8 плюс любой другой РОН и плюс смещение из байта или слова. Любой компонент опять таки может отсутствовать.

    Однако и тут не обошлось без особых случаев.
    Во первых — если в качестве индекса ставится регистр ESP, то индекс обнуляется. ESP не может фигурировать как индекс. Т.е. может, но ведёт это себя так как будто бы индекса просто нет.
    Во вторых — если в качестве базы указан EBP без смещения (наличие смещения тут берется из байта modR/M), то он заменяется на disp32. Т.е. то же самое правило о [ eBP ] что было при чистом modR/M без байта SIB с байтом SIB срабатывает так же.
    Таким образом, если в бинарной форме как бы закодировано [ SCALE * ESP + EBP ], то на самом деле ведёт это себя как [ disp32 ] (ESP просто отваливается, а EBP без смещения сам превращается в disp32).

    Так же в i386 появляется большое число всяких новых вспомогательных регистров и инструкций по работе с ними — все они падают в префикс $0F.

    От 80486 до Pentium

    От i486 до поздних Pentium–ов базовые принципы архитектуры i386 не менялись, а только набухала система команд новыми фичами и возможностями.

    Так, например команды условного перемещения CMOV — все падают в опкоды $0F 40 — $0F 4F.

    В Pentium MMX появляется система команд MMX — все эти команды резонно падают под префикс $0F точно так же.

    Далее появляется расширение SSE1 — в нём одного только префикса $0F перестаёт хватать. И тогда перед этим префиксом стали помещать еще два новых–старых префикса.
    Во первых — префикс $66 который в i386 менял разрядность слова в SSE1 мог уже изменить смысл инструкции.
    Во вторых — префикс $F3 который в i8086 означал префикс REP для строковых инструкций в SSE1 менял поведение инструкций с векторного на скалярное.
    Т.е. инструкция в опкодовой своей части уже могла выглядеть как три байта, например инструкция MOVSS имеет опкод $F3 0F 10 за которым следует байт modR/M.
    Ах да — в таких расширениях системы команд как FPU/MMX и SSE кодировка регистров в байте modR/M вида eAX, eBX, eCX сменялась на соответствующие регистры расширений — регистры FPU (от вершины стека) или регистры MMX/SSE если mm=0. Если же mm>0, то кодировка возвращается к стандартной адресации памяти через возможные индексные и базовые целочисленные регистры и смещения.

    Далее появляется SSE2 и во первых использует опять таки старый–новый префикс $F2 (REPNE) как признак скалярности уже для своих инструкций двойной точности. Во вторых с помощью префикса $66 менял смысл некоторых инструкций SSE1 с одинарной на двойную точность. И в третьих с помощью этого же префикса $66 менял смысл целочисленных инструкций MMX перенаправляя их на свои регистры XMM (в MMX регистры были 64–битные, XMM же — 128–битные).

    SSE3 и SSE4.1 продолжили эту традицию и таким образом комплексная инструкция 32–битных x86 может состоять из целых четырёх байт — пред–префиксов $66, $F2 или $F3, префикса $0F и конкретизирующего опкода. И это не считая того, что могут быть еще уточняющие байты modR/M+SIB!
    Я уже даже не вспоминаю о префиксах типа LOCK.

    Intel x86–64 (64 бита)

    Переход в 64 бита тоже не дался легко.
    Ну, как понятно, регистры теперь могут быть не только 16–битными или 32–битными, но еще и 64–битными. Тогда вместо буквы E в начале они называются как RAX, RBX, RCX и т.д.
    Но что еще важнее: что общее количество РОН повышено с восьми до шестнадцати. Новые регистры называются R8–R15 для 64–битных значений. R8d–R15d для 32–битных, R8w–R15w для 16–битных и R8b–R15b для байт.
    При этом система команд преимущественно сохранилась в неизменном виде.
    Как же не меняя байты modR/M и SIB адресовать эти новые регистры?

    С помощью нового префикса — REX!

    Префикс REX (Register EXtension) занимает целых 16 слотов изначальных однобайтовых опкодов $40–4F. Если вернуться в первую таблицу, то мы увидим, что это все варианты INC/DEC над регистрами общего назначения.
    Да, эти инструкции в x86–64 выпилили заменив префиксом REX. В битовом представлении он выглядит как
    где WRXB — это биты расширяющие инструкцию и её байты modR/M+SIB.
    Бит W равен 1 если мы работаем с 64–битным регистром или ячейкой памяти. Т.е. как только мы выполняем 64–битную целочисленную инструкцию — мы обязаны добавить к ней байт REX (если же нужно 16 бит, то по старинке используется префикс $66).
    Бит R расширяет поле GGG из modR/M до четырёх бит — т.е. шестнадцати РОН.
    Бит X раширяет поле Index из SIB так же до шестнадцати РОН.
    И, наконец, бит B расширяет или поле Base из SIB, если он есть, или поле EEE из modR/M.
    Таким образом как только в инструкции в любом месте встречается регистр из расширенного набора — она обязана будет иметь префикс REX.

    Так же в x86–64 опять немного модифицировался смысл байта modR/M. Тот случай когда в modR/M как бы закодирован [ RBP ] и который на самом деле означает [ addr32 ] здесь изменён на [ RIP + offs32 ], т.е. работу по смещению относительно текущего счётчика инструкций.
    Такого режима работы с данными очень не хватало в x86 для качественной реализации так называемого Position Independed Code (PIC). Здесь же такой режим вменён как бы по умолчанию.
    Чтобы вернуться к обычному [ addr32 ] тут надо просто активировать байт SIB и выбрать такой режим в нём — здесь уже такая подмена не сработает.

    Префиксы VEX и EVEX

    Эти справочные материалы останавливаются на SSE 4.1.
    Но уже в далёком (ох время летит) 2008 году Intel предлагает новый пакет векторных регистров и инструкций — AVX.
    И с AVX им приходит в голову очевидная мысль — что–то префиксов развелось как собак нерезанных. Давайте ка их… причешем!
    И так рождается следующая мысл — префикс VEX: en.wikipedia.org/wiki/VEX_prefix

    Если вкратце, то префикс VEX запрессовывает в неиспользованные в x64 опкоды $C4 и $C5 (ранее в i386 это были команды LDS и LES — быстрой загрузки длинного адреса в пару из РОН и сегментного регистра) сразу несколько интересных бит:
    а) биты из префикса REX (как будто бы он присутствует)
    б) признак того что как будто бы присутствуют префиксы $F2, $F3 или $66 (один из них).
    б) признак что как будто бы присутствует префиксы $0F или $0F 38 или $0F 3A (последние два введены в SSE 4.1 опять таки за недостатком свободных префиксов и опкодов).
    в) и в плюс к тому биты нужные к тому чтобы расширить modR/M под много новых векторных регистров собственно AVX
    Есть короткая и полная формы VEX–префикса:
    короткая: $C5 XX
    полная: $C4 XX XX

    Т.е., четыре байта префиксов из выше заменили на три байта в худшем случае…
    А еще через пять лет в 2013 году Intel предлагает новое расширение к AVX — AVX–512 и… префикса VEX перестало хватать.
    И был предложен префикс EVEX из четырёх уже байт: en.wikipedia.org/wiki/EVEX_prefix
    Он уже использует для своего старта опкод $62 (ранее в i386 инструкция BOUND) + 3 байта битовых полей которые покрывают и AVX и AVX–512.

    В общем глядя на всё это у меня лично остаётся ощущение, что архитектуру x86 при переходе на x86-64 надо было полностью переписать по части кодирования инструкций чтобы префикс не погонял префиксом так отчаянно и на каждом шагу. :)
     
    Intro, R81..., Artem_N и ещё 1-му нравится это.
  2. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    1.995
    Например как? Удлиннив поле номера регистра с 3 до 4 бит уменьшить поле опкода, а с ним набор инструкций в 2 раза? SIB легким движением руки превратить в IB? Так или иначе инструкции один хрен стали бы длинней, а длина инструкции все равно была бы вынуждена быть кратной 8 битам. До 4 байт префиксов для экзотических подсистем вообще погоды не делают: их удельный вес в усредненной программе - величина, неотличимая от нуля.
     
  3. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    Слишком много текста, правильная матчасть тут. Нужно помнить что кодировка инструкций зависит от режима.
     
  4. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    457
    Нет, правильная матчасть тут: ref.x86asm.net
    А интели так и не сподобились сделать внятную таблицу из своих кишмишей и чем больше систему команд развивали тем больше понимали что эта затея провальная, т.к. наговнородили префиксов как собак нерезанных и даже три таблички из Z80 им не помогли бы никак. У них всегда была не документация, а неструктурированное говно. А таблицы они вообще не умели рисовать - руки видимо поотрывало.
    Честно говоря так говнодокументировать как они сделали - это надо особым дауном быть. Но они смогли.
    --- Сообщение объединено, 6 окт 2021 ---
    Например ты не дочитал до конца - до префиксов (E)VEX?
    Ведь сами интели поняли какое говно создали и начали делать (E)VEX и...
    Конечно же получилось говно.
     
    Последнее редактирование: 6 окт 2021
    Research нравится это.
  5. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    aa_dav,
    Возможно, пригодится
    [​IMG]
    To make it clearer, consider the command code jns == 79h
    code
    mnemonichexbinabcinvert
    jns short
    79​
    0111​
    1001
    jns long0F890000.1111.10001001
    setns0F990000.1111.10011001
    cmovns0F490000.1111.01001001
     
    Aiks, R81... и aa_dav нравится это.
  6. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    457
    По части того как оптимизировать схему - ну например в первом же байте инструкции сделать двухбитовое поле XY которое бы обозначало сколько байт занимает опкод - от 1 до 4 байт.
    Как то вот так, а не вот это вот (E)VEX которое заранее отжирает байт на сам признак того что оно "началося" (62/C4/C5).
     
  7. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.348
    aa_dav,
    Рассматривать инструкции как байты в хексе подходит только для систем с небольшим набором команд: контроллеры там всякие, виртуальные машины и т.п. У интела и амд около 2к инструкций, и чтобы понять логику их кодирования, надо смотреть на них исключительно в бинарной форме. Не "опкоды 0x00, 0x01 делают одно, а 0x02, 0x03 - другое", а "бит направления d", "бит размера регистра w", "бит знакового расширения s"... Иначе оно, конечно же, как каша выглядеть будет.
     
    Artem_N нравится это.
  8. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    457
    Но ведь как кашей оно выглядит именно в документации Intel где все инструкции будучи упорядочены либо по алфавиту либо по опкодам превращаются в мешанину.
    А если смотреть в табличном виде как по первой ссылке - как раз происходит упорядочивание и понимание как ложатся опкоды на инструкции.
    А следующие ссылки позволяют разрулить не совсем очевидные случаи как правило связанные с префиксами которых тут как блох.
     
  9. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.348
    aa_dav,
    Документация интел - это справочник по инструкциям, что они делают и какие у них операнды. Это не дока по логике кодирования опкодов.
     
  10. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    457
    Я повторю самое начало этого топика, т.е. самое начало статьи: ЭТО ТО ЧТО МНЕ НЕ ДАВАЛО ПОНЯТЬ ПРИНЦИПЫ КОДИРОВАНИЯ ИНСТРУКЦИЙ X86 ДОЛГИЕ ГОДЫ.
    Говнодокументация от интел - это то что ставило всегда палки в колёса, да.
     
  11. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
  12. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    rmn,

    Там же базовые опкод таблицы. Набрутил я например в 11 году опкод 0F FF, оказалось там есть modrm. Как оказалось и это описано в доках(ud0) :)
     
  13. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    457
    Типичная ситуация после чтения мануалов от Intel. Ты в них чего то перебором набрутил - потом оказалось что в них описан более полный класс команд, но почему и как ты так и не понял.
    Потому что говнодоки от Intel.
    Единственное что они дают понять: что там до тебя описали то что ты выстрадал.
    Таки да.
     
  14. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    1.995
    Иронично. Именно в последнем моем предложении об этом и написано.
     
  15. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    457
    Ткни в конкретное место где ты это так иронизируя утверждаешь. Вот прям место и предложение. Ибо нет их.
    Не сможешь - сам себе судья.
    Т.е. ты (f13nd) звездоболлище. Не звездобол, а звездоболлищее.
     
  16. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    1.995
    Мм, да тут племянник крелка в профессию подался.
     
  17. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    1.995
    Видимо в этом твоя проблема и есть. Был такой древнегреческий мужик Аристотель, который сформулировал классическую композицию спора: тезис, аргумент, критика тезиса, критика аргумента, критика призванных авторитетов, критика пресупозиции. И цель дискуссии не бросание гавном, а выявление самой сильной позиции (тезис+аргумент), которая с огромной долей вероятности окажется истиной. Ты в своем развитии остановился на формулировании тезиса и слабеньких аргументов, а то, что дальше предполагается какая-то критика, для тебя оказалось неожиданностью.
     
  18. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    aa_dav,

    > Ты в них чего то перебором набрутил

    Был реализован декодер длин, довольно интересный метод. Набрутилась инструкция, она описана в манах:

    Код (Text):
    1. 0F FF /r UD0 r32, r/m32
    Я сегодня вспомнил, тк было обсуждение на кл по детекту вирты, вот я подумал что страничный фаулт можно использовать. Но облом не прошло.

    Система кодирования инструкций сложна, это не спекки где линейная кодировка 8-битка. А есчо могут быть фиксы микрокодом.

    > Т.е. ты (f13nd) звездоболлище. Не звездобол, а звездоболлищее.

    Ты давай как то мягче, у f13nd скилл больше чем у тебя и вообще о чём спор. Не нравится архитектура не юзай.
     

    Вложения:

    • rlde.7z
      Размер файла:
      16,9 КБ
      Просмотров:
      213
  19. Intro

    Intro Active Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    600
    Реально замороченный х86, на практике мне в этом чуде пришлось ковыряться, когда в делфи надо было инструкцию popcnt закодировать.
    А так, не очень давно писал код для 8086(286), mov ax, [dx+bx] и при компиляции UASM не выдавал ошибки, но программа конечно не заработала, понятно почему, начисто забыл что в 8086 ограничен набор индексных и базовых регистров.
     
    R81... нравится это.
  20. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    457
    Нет ничего удивительного когда в сообществе умных людей каждый мнит себя великаном.
    Я с этих позиций и привык всех мнимо возвеличенных крыть дальше чем вижу, ибо иначе с такими Линусами невозможно.
    Иногда ошибаюсь, но чаще всего это работает.
    --- Сообщение объединено, 7 окт 2021 ---
    В прошлом году MS раскрыли исходники Microsoft Basic (в версии GW): https://github.com/microsoft/GW-BASIC
    Там можно найти такой комментарий:
    ; Convenience routine to skip a variable's name pointed to by HL.
    ; Returns VALTYP in A with the zero flag set if it is a string.

    Всё бы ничего, но у Intel 8086 не было регистров HL и A — это регистры Intel 8080, того самого предка Zilog Z80. Действительно в файле OEM.H есть некие упоминания поддержки CP/M, а эта операционка базирована на i8080. Таким образом явно торчат уши легаси–кода на котором MS и начала строительство своей империи — васики под самые разные компьютеры и системы и в первую очередь как раз на базе i8080 для Altair 8800.
    При этом чётко отслеживается одна из обозначенных на википедии цель совместимости между i8080 и i8086 на уровне исходных кодов. Всё просто получается, если следующим образом транслировать РОН:
    A –> AL
    BC –> CX
    DE –> DX
    HL –> BX
    Тут скорее всего неслучайно, что HL транслируется в BX, не по порядку, т.к. в i8080 с HL было больше всего адресаций и в i8086 больше всего адресаций было у регистров BX, BP и SI, DI, но при этом только у BX была возможность легко работать с верхним и нижним байтами.
    В общем всё логично.
    --- Сообщение объединено, 7 окт 2021 ---
    Я сегодня внимательно просмотрел весь топик и понял, что ты реально не предоставил ничего своему бесмысленному:
    Ты так бравировал на прошлой странице, что мол сказал и меня упрекал:
    А ведь нет, не было ничего тобой сказано никогда про AVX или расширенные наборы инструкций.
    Ты поэтому так некрасиво начал на племянников твоих сьезжать и сыпать нерелеватной хернёй.
    Я был прав с самого начала - ты зазнайка потерявшая понятия в том где как и о чём ей можно говорить.