Search
Write a publication
Pull to refresh

Comments 28

mov al,byte ptr[eax+edx*N] это настолько прекрасно, что испытываешь чувство схожее с катарсисом.
Классика жанра в том, что когда такое видишь в дизассемблере первое, что приходит в голову — «опять данные дизассемблирую» или «промахнулся мимо начала инструкции»
А можно теперь пояснить для тех, кто не испытал катарсис, но тоже хочет? :) Я понимаю, что эта инструкция загружает в al один байт, лежащий по адресу, значение которого равно eax+edx*N (так ведь?), но ЧТО это значит?

А вообще, это действительно интересно, я видел, что компиляторы применяют инструкцию lea вместо арифметики, когда нужно что-то умножить и сложить, но не знал, что один-единственный mov тоже умеет вычислять такую арифметику.
Для тех кто не разбирается в ассемблере скажите, вот этот плюс и знак множества это же целые операторы, результат вычисляется на стадии компиляции?
Да вы не парьтесь, в этом ассемблере уже почти никто не разбирается.
PS Берется текущее содержимое 32-разрядных регистров EDX и EAX, над ними проводятся соответсвующие арифметические манипуляции. Получившийся результат используется как адрес памяти по которому нужно взять значение и положить в регистр AL. Но не все 32 бита, а только 8. Потому что AL — это младшие 8 разрядов регистра EAX. Вроде ничего не перепутал.
Естественно это все происходит на этапе выполнения с использованием тех значений, которые оказались в регистрах на данный конкретный момент.
результат вычисляется на стадии компиляции?

Компиляция — не вполне правильно.
В большинстве диалектов ассемблера для x86..64 есть макроподстановка и непосредственная трансляция в машинный код (плюс, связывание, конечно).
А это — вариант базово-индексной адресации.
Т.е., это не constexpr в C++ и не вычисление транслятором, а компоновка из данных значений нужной команды: транслятор не вычисляет ничего.

Так а транслятор переведет эти mov в разные инструкции в машинном коде же?

В зависимости от типа операндов машинный код будет разным для инструкций.

Тогда это не татак интересно уже, хоть и забавно.

mov — это и есть инструкция в машинном коде.

Эта программа переводит .c код (или обычный машинный код) в машинный код, который состоит только из одной команды move но с разными аргументами.

И штука реально прикольная )
Не совсем, зависит от того, что вы называете машинным кодом. mov это инструкция языка ассемблера, ассемблер — сборщик машинных кодов. Язык ассемблера != машинный код хотя бы потому, что один текстовый, а второй бинарный, но на этом отличия не заканчиваются. Как бы абсурдно не звучало, но сам ассемблер появился как инструмент программирования под разные процессоры, фактически кроссплатформенный тулчейн для языка ассемблера.
Ну во-первых, здесь пока говорится только про x86.
И про обычный (а не макро) ассемблер, у которого каждая мнемоника имеет вполне четкий байт-код, т.е. мнемоника (ну или команда, или инструкция) — это просто название для соответствующей кучки кодов, в которую она всегда однозначно превращается, и по которой и работает процессор.

Поэтому мнемоника mov имеет вполне четкое отражение 1-в-1 в машинном коде. посмотрите на дамп вверху (думал что там коды, а это адрес :)).

Если взять код программы уже в mov-ах, то там будет постоянно повторятся один и тот-же код каждую новую строчку — это и есть код мнемоники mov, а дальше будут идти ее аргументы.

Это если просто, а если сложно, то код mov в x86 тоже не постоянный и варьируется, т.к. это указывает на тип mov операции, например «88» это «mov [di],dl» а «89» это «mov [si],cx» (коды по памяти, могу ошибаться с точным значением).

Т.е. в обычном асме одна команда = одному набору кода + коды аргумента.

Просто конкретно для mov — это интервал кодов, т.к. туда зашит тип mov — пересылка ли из регистра в регистр, или, например, косвенная пересылка и тд.

Обычный асм — только для конкретной системы, пример — аналог команды асма «mov» x86 для Z80 будет «ld» с абсолютно другим кодом — т.е. и код будет отличаться и мнемоника.

А вот макро-ассемблеры и тд — там уже да, может быть и под разные системы и т.д.

Вот такая вот штука.

Ну, в целом, это примерно то же самое что я и говорил, но тем не менее, машинный код инструкции — это не просто число — это адрес в таблице соответствия инструкции в процессоре. И mov с кодами 88-89 (я тоже не помню уже коды по памяти) — это две разных команды, которые пойдут на разные группы транзисторов в процессоре.


И, кстати, вы же рассматриваете всерьез идею перейти только на mov-инструкции по причинам, которые перечислены в статье? В статье есть замечание что это просто игрушка. Выглядит забавно, доказывает что-то, но новые инструкции вводят в процессор не просто так. Если новая инструкция может сэкономить полтакта на частых операциях — это уже один из поводов ее добавить. По крайней мере на CISC так точно было (привет, Pentium MMX).

И mov с кодами 88-89 (я тоже не помню уже коды по памяти) — это две разных команды, которые пойдут на разные группы транзисторов в процессоре.
Группа команд одинаковая «mov», а вот тип команды да — разный. Как уж там куда в процессоре пойдет — зависит от того, как конвеер сделан, например mov для регистр-в-регистр, ЕМНИП, будет выполнятся одинаковым конвеером (ну или транзисторами :))

А Долан как раз про это и писал — по сути группа mov — это отдельный ассемблер в ассемблере )

И, кстати, вы же рассматриваете всерьез идею перейти только на mov-инструкции по причинам, которые перечислены в статье?
Я это рассматривал как один из вариантов обфускации ну и просто как забавный факт.
UFO landed and left these words here

Неужели неинтересно посмотреть производительность такого кода?

UFO landed and left these words here
Спасибо. А как делать сравнение? Например, для чисел 11 и 100 все единицы перезапишут нули и мы получим 111 вместо 100.

Да, про сравнение тоже не понял. Но сравнение предполагает условный переход, а, вроде бы, было заявлено, что программа получается вообще без ветвлений.

Сравнение делается через вычитание, а вычитание через сложение и представление в дополнительном коде.

Сравнение не так работает.
Для 11 и 100:
mov в ячейку памяти #11 значение 0
mov в ячейку памяти #100 значение 1
mov в регистр данные из ячейки #11

Если бы числа которые мы сравниваем были одинаковые, то в регистре оказалась бы 1, в нашем же случае в ячейке так и лежит 0.

Покажите пожалуйста компилируемый в Nasm пример сравнения…
Код из статьи грешит тем, что нельзя присвоить число по адресу? Или в скобах — это не операция взятия адреса? artiom_n
mov [x], 0
mov [y], 1
mov R, [x]

Не пойму, как работает вызов функции? чем оно заменяет jump? Вроде нельзя же занести через mov адрес в EIP?

А вот, видимо, нет там jmp.


Код выполняется без ветвлений

Согласно идее Стивена, правильно написанный блок кода может либо что-то делать, либо не делать, в зависимости (только!) от исходного состояния системы. То есть ветвление отсутствует как класс, если абсолютно все инструкции исполняются последовательно.
ну вызов функции как-то же должен происходить. Свои функции ладно, допустим он парсит и инлайнит их. А системные вызовы?

Не спрашивайте меня, я сам не понял. Ассемблер, на котором писал я 20 лет назад выглядел слегка по-другому :)
Если вам интересно — там в статье есть ссылка на ютуб и на гитхабе есть pdf с объяснениями (но я тоже ничего не понял).

Sign up to leave a comment.