Войти
ФлеймФорумЖелезо

Блеск и нищета 8/16-битных консолей и ПК (109 стр)

Страницы: 1108 109 110 111 112 Следующая »
#1620
22:20, 19 июля 2021

Dmitry_Milk
По моему длинное сложение с длинной константой вполне себе реальная вещь.


#1621
23:21, 19 июля 2021

=A=L=X=
> с длинной константой

Длинное magic number в программе?

#1622
(Правка: 5:44) 5:37, 20 июля 2021

Dmitry_Milk
> Длинное magic number в программе?

Magic ему вовсе необязательно быть - это может быть вполне символическая константа, но в машкоде собирающаяся как immediate (операторы LOWORD/HIWORD).
Это в принципе конечно мелочи, особенно сейчас, но в 16-битные времена без кешей-мешей вполне себе оптимизация иметь возможность сложить по длинному с immediate. 8086 был 16-битным и 32-битная арифметика вполне себе штука практичная. Так почему бы не сложить с 100000 через add/adc с immediate?

#1623
(Правка: 8:01) 7:58, 20 июля 2021

Еще показалось интересным как в i86 (согласно вышеприведённым ссылкам) шло расширение системы команд с появлением новых поколений процессоров.

В 8086 инструкции состояли из опкода + очень часто байта modR/M который уточнял мощные режимы адресации + непосредственные данные.
Эффективная часть кода операции таким образом была 1-2 байта (опкод + опциональный modR/M). Большинство значений опкодов было плотно занято.

Насколько я понимаю в 286 потребовалось ввести много административных инструкций по управлению защищёнными сегментами и таблицами дескрипторов.
Их префиксировали неиспользованным в 8086 опкодом 0F - и с этим пор префикс 0F стал главным префиксом многобайтовых инструкций вплоть по до сих пор.
Например команда LSL (Load Segment Limit) - это опкод 0F 03.
Что интересно - и в этих инструкциях байт modR/M бывает что используется и может уточнять не только адрес операнда, но и суть команды ровно как в командах прокрутки из примеров выше.

Мимоходом можно заметить, что еще в 8086 команды мат-сопроцессора префиксировались ESC-инструкциями из диапазона D8-DF и так оно и осталось, но это другая песня.

В 386 как еще пример была добавлена целая группа так называемых Control Registers и команды по их загрузке/сохранению 0F 20 - 0F 23.
Но главное что в защищенном режиме 386 байт modR/M меняется - ради новых мощных режимов адресации и равноправия в них всех регистров общего назначения становится возможным включить режим с байтом SIB в котором, например, указывается множитель 2/4/8 - все режимы адресации типа [ 2 * EAX ] автоматически значат, что в инструкции есть байт modR/M с байтом SIB (2 байта уточнения режима адресации в нагрузку к опкоду).

Еще 386 использовал вроде бы неиспользованные опкоды 66 и 67 как временные модификаторы размера операндов (WORD/DWORD) и адресов. Они тоже могут утяжелять размер команды.
С учётом этого новых опкодов 386 добавил не особенно много - между PUSHA и PUSHAD нет разницы по опкоду - может просто быть (или нет) префикс смены размера операндов.
В основном из нового это больше административные инструкции уровня ядра.

MMX (Pentium) это просто продолжение использование префикса 0F, например PUNPCKLBW это 0F 60.
Группа инструкций CMOV (Pentium Pro) это 0F 40 - 0F 4F.

А вот в SSE1 судя по всему опкодов после префикса 0F опять стало не везде хватать и в некоторых случаях появляется новое префиксирование.
Например команда MAXSS это F3 0F 5F (в то время как MAXPS это просто 0F 5F).
F3 в оригинальной системе команд это ничто иное как префикс REP - т.е. признак повторения для блоковых/строковых инструкций. Разумеется в рамках команды SSE он не имел смысла и таким образом был заюзан как признак смены команды с packed на single по аргументам.
Так же активно используется со времён SSE1 префикс 66 - это смена размера параметра в оригинале (тут он часто делает то же самое).

В SSE2 в той же манере заюзали префикс F2 (REPNE - изначально повторение блоковых/строковых инструкций с условием).
Например DIVSD это F2 0F 5E.
Так же на первый взгляд мне показалось, что префикс 66 здесь очень часто как бы переключает команду существовавшую еще в MMX в режим SSE2, т.е. она начинает работать с тем же смыслом, но с регистрами SSE2.

SSE3 добавил мало команд, так что в целом всё то же самое.

Больше http://ref.x86asm.net/coder32.html вроде ничего особенного нет.

Вот как то так и живём. :)

Но уже, конечно, 64-битный режим вовсю и что в нём творится не знаю, но насколько знаю там тоже модифицировано поведение modR/M, но все опкоды стараются быть максимально обратно совместимыми по смыслу и поведению.

#1624
9:20, 20 июля 2021

И еще один забавный фактик - разглядывая http://sparksandflames.com/files/x86InstructionChart.html я заметил еще такую штуку, что оказывается с самого появления 8086 уже сильно-сильно задвинул роль аккумулятора в AX на задний план. Весь спектр арифметико-логических операций может выиграть от использования AL/AX по размеру только если второй операнд - непосредственное данное. Во всех остальных случаях не имеет значения AX или BX. В остальном AX конечно много где играет роль единственного аргумента, но это достаточно редкие по использованию команды типа ввода-вывода из портов или десятичной коррекции. Ну и еще команда XCHG с EAX и РОН короче на байт, чем общий случай.
Т.е. в принципе это настолько уже становится незначительным, что, имхо, уже следовало скидывать аккумулятор с постамента полностью уже тогда.

#1625
14:01, 21 июля 2021

А, вот что я еще проморгал выше.
С введением SSE3 и SSE4 префиксы 0F еще удлинились до последовательностей 0F 38 и 0F 3A. При этом и они еще могут иметь перед собой модификаторы или 66 или F2.
Т.е. полная длина кода инструкции (без учёта всяких там переопределений битности) может быть 4 байта - 3 байта префикса и 1 байт операции.
Мда уж...

#1626
16:56, 21 июля 2021

=A=L=X=
> Т.е. полная длина кода инструкции (без учёта всяких там переопределений
> битности) может быть 4 байта - 3 байта префикса и 1 байт операции.
> Мда уж...
Важно ли это, если считывание этих 4-х байт в современных процессорах происходит на 64-х битной шине, а как с ними уже поступает микропрограммный автомат процессора так ли важно, если производительности железа хватает для решения потребительских задач?

#1627
(Правка: 17:13) 17:12, 21 июля 2021

KPG
> Важно ли это, если считывание этих 4-х байт в современных процессорах
> происходит на 64-х битной шине, а как с ними уже поступает микропрограммный
> автомат процессора так ли важно, если производительности железа хватает для
> решения потребительских задач?

Тут решает суммарная длина всего потока команд - их плотность. За это бьются как раз вплоть до того, что ARM в режиме Thumb поимел команды переменной длины и растерял вроде бы даже половину регистров.
У x86 уже столько наросло префиксов, что по моему очень хорошая идея просто полностью пересмотреть бинарное кодирование команд сделав базовый код инструкции двухбайтовым (минимальная длина команды) так чтобы префиксы стали либо не нужны либо в крайне редких случаях.

Вот, кстати, 64-битный режим x86-64 тоже забавный - все же знают что количество регистров общего назначения (РОН) выросло с 8 до 16? Так вот опять таки открыл для себя, что не совсем дёшево это прошло - в вышеупоминаемом часто байте modR/M под код двух регистров в двух битовых полях отведено по 3 бита - откуда собственно и берётся 8 РОН, так вот чтобы адресовать регистры R8-R15 надо вставлять перед командой так называемый префикс REX в котором есть 4 служебных бита (префикс REX отнимает 16 основных команд инкремента/декремента как раз РОН за 1 байт инструкции, коды от 40 до 4F) и просто два из них дополняют эти 3-битовые поля в modR/M таким образом делая их 4-битными. Т.е. как только в инструкции проскакивает R8-R15 - это значит что перед инструкцией есть байт REX.
Еще и REX ко всему что описано выше. Он именно как пришлёпка сверху обязательная.
Т.е. выгодно не вылазить за пределы первых восьми РОН даже в x86-64 где шестнадцать РОН.

#1628
17:24, 21 июля 2021

=A=L=X=
> Т.е. выгодно не вылазить за пределы первых восьми РОН даже в x86-64 где
> шестнадцать РОН.
Почему выгодно, если это не сказывается на производительности 64-х  приложений в сравнении с 32--х битными?

#1629
17:34, 21 июля 2021

KPG
> Почему выгодно, если это не сказывается на производительности 64-х приложений

Так сказывается же - _каждая_ инструкция где есть регистры R8-R15 длиннее на 1 байт. Каждая во всём потоке команд. 10 команд - значит 10 лишних байт будет в потоке по сравнению с ситуацией когда R8-R15 не используются.

#1630
17:36, 21 июля 2021

KPG
> в сравнении с 32--х битными?

Имхо если переделать, то можно уплотнить и ускорить и по сравнению с x86-64.

#1631
(Правка: 10:44) 10:40, 22 июля 2021

Блин, уже оффтоп идёт, но раз начал сюда писать так и продолжу пока. Потом подчищу.

Итак, если инструкции i386 нужны аргументы сложнее чем EAX с непосредственным данным, то сразу за кодом инструкции идёт байт ModR/M описывающий аргументы.
Его двоичный формат:
mmRRRMMM, где mm - это четыре режима, RRR - это код регистра общего назначения который всегда должен быть один из аргументов и MMM - второй аргумент с учётом модификатора mm (может ссылаться на память).
Коды регистров 0-7 по порядку следующие: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI (заметьте, что a,c,d,b идут совсем не по порядку, что намекает, что в кишках "всё не так однозначно").
Если mm=11, то MMM это тоже код регистра и значит всё просто - операция вида ADD EAX, EBX.
Однако если mm имеет другие три значения, то разыгрывается косвенная адресация с возможным смещением.
При mm=00 смещения нет, при mm=01 смещение 8-битное и при mm=10 смещение 32-битное. Общая формула: [ REG (+ disp8/32) ], где смещение опционально.

Но есть два исключения: во первых, если mm=00 и регистр - EBP, то формула внезапно превращается в [ disp32 ]. Это просто захардкоженное исключение - нельзя прямо сослаться на [ EBP ] через один modR/M, нужно обязательно добавить нулевое смещение типа [ EBP + 0 ], т.е. изменить mm на 01 например с байтовым смещением.

Во вторых если в качестве регистра в MMM при косвенных адресациях использован ESP, то он тоже не срабатывает, а срабатывает дополнительный режим - сложная адресация через байт SIB. Так и записывается в спеках: [ SIB (+ disp8/32) ].

Байт SIB (scale/index/base) идёт в этом случае сразу вслед за байтом modR/M имеет следующий двоичный формат:
SSIIIBBB
Это когда режим адресации имеет вид [ 4 * EAX + EDX (+ offset) ]. Есть offset или нет и какой у него размер берётся еще с предыдущего шага - из байта modR/M по полю mm.
Scale - 1/2/4/8 это биты SS.
Ну а III и BBB это вновь и снова коды регистров - индекса и базы. Именно индекс может умножаться на Scale в общей формуле [ SS * III + BBB + OFFS ].

Но не без исключений! xD
Во первых - если в качестве индекса ставится регистр ESP, то индекс обнуляется. ESP не может фигурировать как индекс. Т.е. может, но ведёт это себя так как будто бы индекса просто нет.
Во вторых - если при mm=00 в качестве базы указан EBP, то он заменяется на disp32. Т.е. то же самое правило что было при чистом modR/M без байта SIB с байтом SIB срабатывает так же.

Таким образом, если в бинарной форме как бы закодировано [ SCALE * ESP + EBP ], то на самом деле ведёт это себя как [ disp32 ] (ESP просто отваливается, а EBP без смещения сам превращается в disp32).

Интересно, что в i86-64 этим дублированием воспользовались вот как: режим адресации через один modR/M когда в двоичном коде указан [ EBP ], что срабатывает как [ disp32 ] превратили в очень полезный для позиционно-независимого кода режим [ RIP + disp32 ], т.е. адресацию относительно IP, а IP никак иначе больше не виден.
Но если нужен именно [ disp32 ], т.е. абсолютная адресация памяти, то нужно задействовать байт SIB и его режим [ EBP ] который срабатывает как [ disp32 ] уже поведёт себя как раньше.

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

#1632
(Правка: 16:50) 16:47, 22 июля 2021

Немного разбавлю онтопиком :)

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

+ О чём вообще речь

#1633
(Правка: 12:18) 12:10, 23 июля 2021

=A=L=X=
Oчень удивительно, что Вы этим заинтересовались только сейчас.
Лет 25 тому назад я принялся осваивать 1810ВМ86 и удивился: Какая же это мусорка! (Против z80)
Столько эзотерики во всех этих MOD/RM полях, что поперхнуться можно.
Хотя, эмулятор 15 лет назад писал и удивился: Написал эмулятор x86-кода быстрее, чем тот же  i8080. Он проще как будто, если не утруждаться всякими механизмами защиты и памяти.

А 12 лет назад, ради прикола, набросал свою концептуальную систему команд x86 с кучей префиксов:

+ Показать

#1634
12:18, 23 июля 2021
+ Показать
Страницы: 1108 109 110 111 112 Следующая »
ФлеймФорумЖелезо