Низкоуровневое программирование для Дzenствующих

Вид материалаДокументы

Содержание


I.4. Благодарности
MASM, и его разрешении принадлежит Four-F
FatMoon, Rustam
II. Лень – двигатель Макро
Подобный материал:
1   ...   9   10   11   12   13   14   15   16   ...   42

I.4. Благодарности


Не могу не написать этот пункт, ибо не только автору обязана эта статья.

Она обязана замечательной версии Win98 с инсталляцией от 2000, которая отформатировала весь мой винчестер, и унесла в небытие первый вариант настоящей статьи. :)

Не малая заслуга в вопросе терминологии MASM, и его разрешении принадлежит Four-F, который как он сам мне признался, съел на макросах собаку, при чём без соли :).

Когда я думаю, чтобы было бы без самого Маниакального редактора в Inet, CyberManiacа, то понимаю: без его правок мои статьи приводили бы в ужас, и лишали разума всех морально неустойчивых читателей. CyberManiac: «Только такой замечательный безумец как ты может выдержать ЭТО!!!» :).

FatMoon, Rustam, The Svin – вы дали понять мне то, что такая статья действительно нужна, и это, наверное, самое главное. Вряд ли я бы так долго работал над ней, если бы меня никто не подталкивал.

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

С уважением, Edmond/HI-TECH

II. Лень – двигатель Макро


Когда говорят, что лень – это двигатель прогресса, видимо лицемерят или преувеличивают. Скорее это нежелание выполнять одну и ту же работу очень часто. Первая парадигма к созданию макро звучит так:

Если есть что-то похожее, что нужно делать очень часто, я могу оформить это как макроопределение.

Ассемблер, дающий программисту полную свободу в использовании методик программирования, совершенно лишает его средств для выражения этих методик. Например, ООП. В MASM32 нет классов, конструкторов и других механизмов, поддерживающих эту абстракцию. Зато вместо ООП Вы можете придумать множество других методик и абстракций (как, например модель серверов).

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

m2m MACRO M1, M2

push M2

pop M1

ENDM


return MACRO arg

mov eax, arg

ret

ENDM

Предположим, что кому-то так надоело писать:

push переменная2
pop переменная1

И он решил придумать макро для этого. Эта пара команд осуществляет пересылку данных из одной ячейки памяти в другую. То есть теперь в программе, когда вы захотите написать push/pop, вы можете заменить это некой m2m операнд1, операнд2. Посмотрите на эти два участка кода:

mov wc.cbWndExtra, NULL
m2m wc.hInstance, hInst
mov wc.hbrBackground, COLOR_BTNFACE+1
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance,
mov wc.hbrBackground, COLOR_BTNFACE+1

Первый вариант не только занимает меньше строк (что тоже важно), но и намного понятнее, чем push/pop (если вы, знаете что такое m2m). Конечно, если говорить о макро m2m, то он имеет и очень важный недостаток.

Мощь макро была бы сказочной, если бы MASM умел следить за кодом, или ему можно было бы указать, что, например, сейчас регистр ebx == 0, или eax никем не используется. Хотя мы попробуем достичь подобного эффекта самостоятельно.

Этот недостаток потеря контроля над оптимальностью кода. Например, более быстрыми, по сравнению с парой команд push/pop, являются mov eax,… Употребляя макро m2m, вы получаете худший код, если стремитесь оптимизировать по скорости. И здесь есть две стороны проектирования кода:
  1. Эффективность кода
  2. Совершенство стилистики

Используя макро m2m, вы повышаете уровень стилистики, так как сокращаете время на понимание исходного кода (вами же или другим программистом). Однако с другой стороны вы теряете эффективность.

Это одна из вечных задач архитектора – найти баланс между эффективностью в коде и совершенством стилистики.

Другая парадигма использования макро звучит так:

Если, объединяя что-то в одно целое, я улучшаю стиль кода – это можно сделать в виде макроопределения.

Эта парадигма отличается от предыдущей тем, что создание макроопределения обуславливается только улучшением стилизации кода, и не имеет особой практической ценности. Например, я определил такие макро для определения кода начала и конца в главном модуле программы:

$$$WIN32START macro
PUBLIC l$_ExitProgram
_start:
xor ebx,ebx
endm

$$$WIN32END macro
l$_ExitProgram:
push $$$__null
call ExitProcess
end _start

endm

В этих макро нет по сути никакой пользы, кроме эстетической. Зато, глядя на код, можно сразу понять, что это не что иное, как начало программы нечто вроде main() в C++.

И последняя парадигма использования макро:

Если ты используешь технологию программирования – попытайся заключить её в комплекс макроопределений.

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

Наиболее важная часть использования макро. Посмотрите, например, файл Objects.INC из пакета MASM32 в папке oop (NaN & Thomas).
Мы начнём создание первых макро со следующей задачи.

Наверное, вы знаете, что EXE приложения всегда могут загружаться по адресу равному:

PROGRAM_IMAGE_BASE EQU 400000h

Во-первых, это даёт нам право убрать из приложения всю Relock секцию, тем самым, уменьшив объём образа (если эта секция нужна для систем плагинов, её можно держать отдельно).

Во-вторых мы можем более не вызывать функцию GetModuleHandle, что так же полезно для нас. Использование константы PROGRAM_IMAGE_BASE очень удобно. Однако, что будет значить это удобство, если всё-таки PROGRAM_IMAGE_BASE не определено? Это будет означать, что мы обязаны переписать весь код. А если этого кода много?

Определённо об этом нужно позаботится заранее. Давайте же будем решать эту проблему при помощи макро! Для этого нам станут необходимыми некоторые знания о том, как обрабатывается макро, и что это такое.