Содержание статьи
- Резьба по ассемблерному коду
- Читаем данные из памяти по-новому
- Копируем данные, не используя циклы
- Сравниваем строки, не используя циклы
- Меняем местами значения двух регистров
- Выполняем восьмибитные операции экономно
- Знакомимся с двоично-десятичным кодом
- Умножаем и делим на 10 экономно
- Еще несколько полезных трюков
- Выводы
Другие статьи курса
- Зачем учить ассемблер в 2020 году
- Делаем первые шаги в освоении асма
- Осваиваем арифметические инструкции
- Как работают переменные, режимы адресации, инструкции условного перехода
- Учимся работать с памятью
- Работаем с большими числами и делаем сложные математические вычисления
- Пишем клон игры Flappy Bird, который уместится в бутсектор
- Пишем бейсик и умещаем его в 512 байт
Резьба по ассемблерному коду
Надеюсь, ты знаешь книгу Стивена Леви «Хакеры: герои компьютерной революции». Если нет, обязательно прочти! Сейчас мы с тобой поностальгируем по тем славным временам, которые описывает Леви. В частности, вспомним, чем пионеры хакерства занимались в «Клубе моделирования железной дороги» Массачусетского технологического института и как они кодили.
Хакеры тех времен, корпя над своими программами, пытались выжать из ассемблерных инструкций все, что только возможно, чтобы в итоге программа стала максимально компактной. Попытки отрезать часть инструкций от компьютерной программы без воздействия на конечный результат стали для хакеров навязчивой идеей.
Иногда такая резьба по ассемблерному коду принимала состязательный характер — своеобразное соревнование мачо, призванное доказать себе и другим, что совершенству нет предела. Ты отрезал две инструкции или даже одну? Получи бурные аплодисменты братьев по духу. Ты пересмотрел проблему с нуля, с неожиданного угла зрения и разработал новый алгоритм, который сократил программу на целый блок команд? Испытай катарсис и получи еще более бурные аплодисменты!
Особое рвение хакеры проявляли к оптимизации подпрограммы для печати десятичных чисел. За несколько месяцев они изготовили целую кучу вариаций. С чего вдруг такой интерес именно к этой задаче?
С того, что в MIT действовало негласное правило: «Если ты собственноручно написал подпрограмму печати десятичных чисел, значит, знаешь о компьютере достаточно, чтобы называть себя в некотором роде программистом. Причем, если у тебя ушло на эту подпрограмму около сотни ассемблерных инструкций, значит, ты беспроглядный глупец, хотя и программист. А если написал действительно хорошую и короткую процедуру меньшего размера, то можешь попробовать называть себя хакером».
В конечном счете, попеременно убирая инструкции то в одном, то в другом месте, хакеры допилили процедуру печати десятичных чисел до пятидесяти с небольшим инструкций.
Дальше дело приняло серьезный оборот. Поиск лучшего решения превратился в нечто большее, чем просто состязание, — в поиск святого Грааля. Однако, сколько бы сил ни было потрачено, никому не удавалось преодолеть барьер из пятидесяти команд. И когда практически все уже смирились с тем, что это невозможно, один из хакеров догадался посмотреть на решение задачи под другим углом. В итоге его версия подпрограммы уместилась в 46 ассемблерных инструкций.
До этого знаменательного часа все считали, что оптимальный алгоритм для подпрограммы печати десятичных чисел — это последовательное вычитание, при котором использовались таблицы степеней числа 10. Однако, как оказалось, задачу можно решить и без такой таблицы. Леви, к сожалению, не приводит ассемблерный код в своей книге, поэтому познакомиться с этим шедевром у нас не получится.
Но не спеши расстраиваться. Сейчас я покажу тебе свою версию такой подпрограммы. Она у меня уместилась в 12 инструкций (и 23 байта).
Кому‑то может показаться, что столько заморачиваться ради того, чтобы сократить размер программы, в наши дни уже не имеет смысла. Однако такой навык очень пригождается, когда пишешь какой‑нибудь шелл‑код или редактируешь скомпилированный бинарник. И в том и в другом случае нужно уметь обходиться как можно меньшим количеством ассемблерных инструкций. И сейчас я покажу несколько трюков, которые помогут тебе сокращать размер своих ассемблерных программ.
Читаем данные из памяти по-новому
Во всех предыдущих уроках мы читали память, ссылаясь на нужную нам ячейку через регистр BX
. Примерно вот так.
Но то же самое можно сделать и вот так.
Инструкция lodsb
говорит процессору 8088: «Загрузи байт из адреса, на который указывает DS:
, и сохрани этот байт в регистр AL
. И затем увеличь SI
на единицу (если флаг направления сброшен в 0)».
Еще у 8088 есть инструкция lodsw
, которая работает почти как lodsb
, только загружает из DS:
слово (сдвоенный байт), сохраняет результат в регистр AX
и увеличивает SI
на 2.
Копируем данные, не используя циклы
Зная о существовании инструкций lodsb
/lodsw
и их противоположностей stosb
/stows
, мы можем написать подпрограмму для копирования области памяти.
Этот внутренний цикл занимает всего четыре байта. Но у процессора 8088 есть инструкции movsb
и movsw
, которые делают ту же самую операцию, но при этом не используют регистр AL
или AX
.
Теперь внутренний цикл занимает три байта. Но и это не предел! Мы можем сделать все то же самое без инструкции loop
.
Обрати внимание, что movsb
— это две инструкции в одной: lodsb
и stosb
. И аналогично в movsw
скомбинированы lodsw
и stosw
. При этом movsb
/movsw
не используют регистры AL
/AX
, что весьма приятно.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»